From 457af65df6439bac90dcdaa1231599da4f06874e Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 26 Nov 2025 22:47:48 -0500 Subject: [PATCH] enh/feat: toggle folders & user perm --- backend/open_webui/config.py | 15 ++++++++++++++- backend/open_webui/main.py | 3 +++ backend/open_webui/routers/auths.py | 4 ++++ backend/open_webui/routers/folders.py | 18 +++++++++++++++++- backend/open_webui/routers/users.py | 4 +++- .../components/admin/Settings/General.svelte | 8 ++++++++ .../admin/Users/Groups/EditGroupModal.svelte | 5 +++-- .../admin/Users/Groups/Permissions.svelte | 5 +++-- src/lib/components/layout/Sidebar.svelte | 8 ++++++-- 9 files changed, 61 insertions(+), 9 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index ec62c8ba01..54ca0218d7 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1447,6 +1447,10 @@ USER_PERMISSIONS_FEATURES_CODE_INTERPRETER = ( == "true" ) +USER_PERMISSIONS_FEATURES_FOLDERS = ( + os.environ.get("USER_PERMISSIONS_FEATURES_FOLDERS", "True").lower() == "true" +) + USER_PERMISSIONS_FEATURES_NOTES = ( os.environ.get("USER_PERMISSIONS_FEATURES_NOTES", "True").lower() == "true" ) @@ -1503,12 +1507,15 @@ DEFAULT_USER_PERMISSIONS = { "temporary_enforced": USER_PERMISSIONS_CHAT_TEMPORARY_ENFORCED, }, "features": { + # General features "api_keys": USER_PERMISSIONS_FEATURES_API_KEYS, + "folders": USER_PERMISSIONS_FEATURES_FOLDERS, + "notes": USER_PERMISSIONS_FEATURES_NOTES, "direct_tool_servers": USER_PERMISSIONS_FEATURES_DIRECT_TOOL_SERVERS, + # Chat features "web_search": USER_PERMISSIONS_FEATURES_WEB_SEARCH, "image_generation": USER_PERMISSIONS_FEATURES_IMAGE_GENERATION, "code_interpreter": USER_PERMISSIONS_FEATURES_CODE_INTERPRETER, - "notes": USER_PERMISSIONS_FEATURES_NOTES, }, } @@ -1518,6 +1525,12 @@ USER_PERMISSIONS = PersistentConfig( DEFAULT_USER_PERMISSIONS, ) +ENABLE_FOLDERS = PersistentConfig( + "ENABLE_FOLDERS", + "folders.enable", + os.environ.get("ENABLE_FOLDERS", "True").lower() == "true", +) + ENABLE_CHANNELS = PersistentConfig( "ENABLE_CHANNELS", "channels.enable", diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index af8e670a53..899a3e08e1 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -352,6 +352,7 @@ from open_webui.config import ( ENABLE_API_KEYS, ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS, API_KEYS_ALLOWED_ENDPOINTS, + ENABLE_FOLDERS, ENABLE_CHANNELS, ENABLE_NOTES, ENABLE_COMMUNITY_SHARING, @@ -767,6 +768,7 @@ app.state.config.WEBHOOK_URL = WEBHOOK_URL app.state.config.BANNERS = WEBUI_BANNERS +app.state.config.ENABLE_FOLDERS = ENABLE_FOLDERS app.state.config.ENABLE_CHANNELS = ENABLE_CHANNELS app.state.config.ENABLE_NOTES = ENABLE_NOTES app.state.config.ENABLE_COMMUNITY_SHARING = ENABLE_COMMUNITY_SHARING @@ -1842,6 +1844,7 @@ async def get_app_config(request: Request): **( { "enable_direct_connections": app.state.config.ENABLE_DIRECT_CONNECTIONS, + "enable_folders": app.state.config.ENABLE_FOLDERS, "enable_channels": app.state.config.ENABLE_CHANNELS, "enable_notes": app.state.config.ENABLE_NOTES, "enable_web_search": app.state.config.ENABLE_WEB_SEARCH, diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index 2a73496f3b..24cbd9a03f 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -900,6 +900,7 @@ async def get_admin_config(request: Request, user=Depends(get_admin_user)): "JWT_EXPIRES_IN": request.app.state.config.JWT_EXPIRES_IN, "ENABLE_COMMUNITY_SHARING": request.app.state.config.ENABLE_COMMUNITY_SHARING, "ENABLE_MESSAGE_RATING": request.app.state.config.ENABLE_MESSAGE_RATING, + "ENABLE_FOLDERS": request.app.state.config.ENABLE_FOLDERS, "ENABLE_CHANNELS": request.app.state.config.ENABLE_CHANNELS, "ENABLE_NOTES": request.app.state.config.ENABLE_NOTES, "ENABLE_USER_WEBHOOKS": request.app.state.config.ENABLE_USER_WEBHOOKS, @@ -921,6 +922,7 @@ class AdminConfig(BaseModel): JWT_EXPIRES_IN: str ENABLE_COMMUNITY_SHARING: bool ENABLE_MESSAGE_RATING: bool + ENABLE_FOLDERS: bool ENABLE_CHANNELS: bool ENABLE_NOTES: bool ENABLE_USER_WEBHOOKS: bool @@ -945,6 +947,7 @@ async def update_admin_config( form_data.API_KEYS_ALLOWED_ENDPOINTS ) + request.app.state.config.ENABLE_FOLDERS = form_data.ENABLE_FOLDERS request.app.state.config.ENABLE_CHANNELS = form_data.ENABLE_CHANNELS request.app.state.config.ENABLE_NOTES = form_data.ENABLE_NOTES @@ -987,6 +990,7 @@ async def update_admin_config( "JWT_EXPIRES_IN": request.app.state.config.JWT_EXPIRES_IN, "ENABLE_COMMUNITY_SHARING": request.app.state.config.ENABLE_COMMUNITY_SHARING, "ENABLE_MESSAGE_RATING": request.app.state.config.ENABLE_MESSAGE_RATING, + "ENABLE_FOLDERS": request.app.state.config.ENABLE_FOLDERS, "ENABLE_CHANNELS": request.app.state.config.ENABLE_CHANNELS, "ENABLE_NOTES": request.app.state.config.ENABLE_NOTES, "ENABLE_USER_WEBHOOKS": request.app.state.config.ENABLE_USER_WEBHOOKS, diff --git a/backend/open_webui/routers/folders.py b/backend/open_webui/routers/folders.py index 03212bdb7c..fe2bf367bf 100644 --- a/backend/open_webui/routers/folders.py +++ b/backend/open_webui/routers/folders.py @@ -46,7 +46,23 @@ router = APIRouter() @router.get("/", response_model=list[FolderNameIdResponse]) -async def get_folders(user=Depends(get_verified_user)): +async def get_folders(request: Request, user=Depends(get_verified_user)): + if request.app.state.config.ENABLE_FOLDERS is False: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=ERROR_MESSAGES.ACCESS_PROHIBITED, + ) + + if user.role != "admin" and not has_permission( + user.id, + "features.folders", + request.app.state.config.USER_PERMISSIONS, + ): + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=ERROR_MESSAGES.ACCESS_PROHIBITED, + ) + folders = Folders.get_folders_by_user_id(user.id) # Verify folder data integrity diff --git a/backend/open_webui/routers/users.py b/backend/open_webui/routers/users.py index eddc56d77a..9b30ba8f20 100644 --- a/backend/open_webui/routers/users.py +++ b/backend/open_webui/routers/users.py @@ -219,11 +219,13 @@ class ChatPermissions(BaseModel): class FeaturesPermissions(BaseModel): api_keys: bool = False + folders: bool = True + notes: bool = True direct_tool_servers: bool = False + web_search: bool = True image_generation: bool = True code_interpreter: bool = True - notes: bool = True class UserPermissions(BaseModel): diff --git a/src/lib/components/admin/Settings/General.svelte b/src/lib/components/admin/Settings/General.svelte index a7b62857c0..d46f37a89f 100644 --- a/src/lib/components/admin/Settings/General.svelte +++ b/src/lib/components/admin/Settings/General.svelte @@ -676,6 +676,14 @@ +
+
+ {$i18n.t('Folders')} +
+ + +
+
{$i18n.t('Notes')} ({$i18n.t('Beta')}) diff --git a/src/lib/components/admin/Users/Groups/EditGroupModal.svelte b/src/lib/components/admin/Users/Groups/EditGroupModal.svelte index ef97294c96..d105c75d50 100644 --- a/src/lib/components/admin/Users/Groups/EditGroupModal.svelte +++ b/src/lib/components/admin/Users/Groups/EditGroupModal.svelte @@ -84,11 +84,12 @@ }, features: { api_keys: false, + folders: true, + notes: true, direct_tool_servers: false, web_search: true, image_generation: true, - code_interpreter: true, - notes: true + code_interpreter: true } }; diff --git a/src/lib/components/admin/Users/Groups/Permissions.svelte b/src/lib/components/admin/Users/Groups/Permissions.svelte index 9f0e4ef2e9..892fc6fb03 100644 --- a/src/lib/components/admin/Users/Groups/Permissions.svelte +++ b/src/lib/components/admin/Users/Groups/Permissions.svelte @@ -54,11 +54,12 @@ }, features: { api_keys: false, + folders: true, + notes: true, direct_tool_servers: false, web_search: true, image_generation: true, - code_interpreter: true, - notes: true + code_interpreter: true } }; diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte index ad534dab0d..42e2ad8ded 100644 --- a/src/lib/components/layout/Sidebar.svelte +++ b/src/lib/components/layout/Sidebar.svelte @@ -63,6 +63,7 @@ import Note from '../icons/Note.svelte'; import { slide } from 'svelte/transition'; import HotkeyHint from '../common/HotkeyHint.svelte'; + import { key } from 'vega'; const BREAKPOINT = 768; @@ -90,8 +91,11 @@ } const initFolders = async () => { + if ($config?.features?.enable_folders === false) { + return; + } + const folderList = await getFolders(localStorage.token).catch((error) => { - toast.error(`${error}`); return []; }); _folders.set(folderList.sort((a, b) => b.updated_at - a.updated_at)); @@ -932,7 +936,7 @@ {/if} - {#if folders} + {#if $config?.features?.enable_folders && ($user?.role === 'admin' || ($user?.permissions?.features?.folders ?? true)) && Object.keys(folders).length > 0}