From f69e37a8507d6d57382d6670641b367f3127f90a Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 20 Nov 2025 18:32:34 -0500 Subject: [PATCH] feat/enh: user sharing perms --- backend/open_webui/config.py | 39 +++- backend/open_webui/routers/users.py | 11 +- .../admin/Users/Groups/EditGroupModal.svelte | 5 + .../admin/Users/Groups/Permissions.svelte | 125 +++++++++-- .../Knowledge/CreateKnowledgeBase.svelte | 3 +- .../workspace/Knowledge/KnowledgeBase.svelte | 31 +-- .../workspace/Models/ModelEditor.svelte | 3 +- .../workspace/Prompts/PromptEditor.svelte | 3 +- .../workspace/Tools/ToolkitEditor.svelte | 3 +- .../workspace/common/AccessControl.svelte | 212 +++++++++--------- .../common/AccessControlModal.svelte | 6 +- 11 files changed, 297 insertions(+), 144 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 069cb4c245..02928733d0 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1276,6 +1276,12 @@ USER_PERMISSIONS_WORKSPACE_TOOLS_EXPORT = ( os.environ.get("USER_PERMISSIONS_WORKSPACE_TOOLS_EXPORT", "False").lower() == "true" ) + +USER_PERMISSIONS_WORKSPACE_MODELS_ALLOW_SHARING = ( + os.environ.get("USER_PERMISSIONS_WORKSPACE_MODELS_ALLOW_SHARING", "False").lower() + == "true" +) + USER_PERMISSIONS_WORKSPACE_MODELS_ALLOW_PUBLIC_SHARING = ( os.environ.get( "USER_PERMISSIONS_WORKSPACE_MODELS_ALLOW_PUBLIC_SHARING", "False" @@ -1283,8 +1289,10 @@ USER_PERMISSIONS_WORKSPACE_MODELS_ALLOW_PUBLIC_SHARING = ( == "true" ) -USER_PERMISSIONS_NOTES_ALLOW_PUBLIC_SHARING = ( - os.environ.get("USER_PERMISSIONS_NOTES_ALLOW_PUBLIC_SHARING", "False").lower() +USER_PERMISSIONS_WORKSPACE_KNOWLEDGE_ALLOW_SHARING = ( + os.environ.get( + "USER_PERMISSIONS_WORKSPACE_KNOWLEDGE_ALLOW_PUBLIC_SHARING", "False" + ).lower() == "true" ) @@ -1295,6 +1303,11 @@ USER_PERMISSIONS_WORKSPACE_KNOWLEDGE_ALLOW_PUBLIC_SHARING = ( == "true" ) +USER_PERMISSIONS_WORKSPACE_PROMPTS_ALLOW_SHARING = ( + os.environ.get("USER_PERMISSIONS_WORKSPACE_PROMPTS_ALLOW_SHARING", "False").lower() + == "true" +) + USER_PERMISSIONS_WORKSPACE_PROMPTS_ALLOW_PUBLIC_SHARING = ( os.environ.get( "USER_PERMISSIONS_WORKSPACE_PROMPTS_ALLOW_PUBLIC_SHARING", "False" @@ -1302,6 +1315,12 @@ USER_PERMISSIONS_WORKSPACE_PROMPTS_ALLOW_PUBLIC_SHARING = ( == "true" ) + +USER_PERMISSIONS_WORKSPACE_TOOLS_ALLOW_SHARING = ( + os.environ.get("USER_PERMISSIONS_WORKSPACE_TOOLS_ALLOW_SHARING", "False").lower() + == "true" +) + USER_PERMISSIONS_WORKSPACE_TOOLS_ALLOW_PUBLIC_SHARING = ( os.environ.get( "USER_PERMISSIONS_WORKSPACE_TOOLS_ALLOW_PUBLIC_SHARING", "False" @@ -1310,6 +1329,17 @@ USER_PERMISSIONS_WORKSPACE_TOOLS_ALLOW_PUBLIC_SHARING = ( ) +USER_PERMISSIONS_NOTES_ALLOW_SHARING = ( + os.environ.get("USER_PERMISSIONS_NOTES_ALLOW_PUBLIC_SHARING", "False").lower() + == "true" +) + +USER_PERMISSIONS_NOTES_ALLOW_PUBLIC_SHARING = ( + os.environ.get("USER_PERMISSIONS_NOTES_ALLOW_PUBLIC_SHARING", "False").lower() + == "true" +) + + USER_PERMISSIONS_CHAT_CONTROLS = ( os.environ.get("USER_PERMISSIONS_CHAT_CONTROLS", "True").lower() == "true" ) @@ -1431,10 +1461,15 @@ DEFAULT_USER_PERMISSIONS = { "tools_export": USER_PERMISSIONS_WORKSPACE_TOOLS_EXPORT, }, "sharing": { + "models": USER_PERMISSIONS_WORKSPACE_MODELS_ALLOW_SHARING, "public_models": USER_PERMISSIONS_WORKSPACE_MODELS_ALLOW_PUBLIC_SHARING, + "knowledge": USER_PERMISSIONS_WORKSPACE_KNOWLEDGE_ALLOW_SHARING, "public_knowledge": USER_PERMISSIONS_WORKSPACE_KNOWLEDGE_ALLOW_PUBLIC_SHARING, + "prompts": USER_PERMISSIONS_WORKSPACE_PROMPTS_ALLOW_SHARING, "public_prompts": USER_PERMISSIONS_WORKSPACE_PROMPTS_ALLOW_PUBLIC_SHARING, + "tools": USER_PERMISSIONS_WORKSPACE_TOOLS_ALLOW_SHARING, "public_tools": USER_PERMISSIONS_WORKSPACE_TOOLS_ALLOW_PUBLIC_SHARING, + "notes": USER_PERMISSIONS_NOTES_ALLOW_SHARING, "public_notes": USER_PERMISSIONS_NOTES_ALLOW_PUBLIC_SHARING, }, "chat": { diff --git a/backend/open_webui/routers/users.py b/backend/open_webui/routers/users.py index 52e876ac99..f53b0e2749 100644 --- a/backend/open_webui/routers/users.py +++ b/backend/open_webui/routers/users.py @@ -183,10 +183,15 @@ class WorkspacePermissions(BaseModel): class SharingPermissions(BaseModel): - public_models: bool = True - public_knowledge: bool = True - public_prompts: bool = True + models: bool = False + public_models: bool = False + knowledge: bool = False + public_knowledge: bool = False + prompts: bool = False + public_prompts: bool = False + tools: bool = False public_tools: bool = True + notes: bool = False public_notes: bool = True diff --git a/src/lib/components/admin/Users/Groups/EditGroupModal.svelte b/src/lib/components/admin/Users/Groups/EditGroupModal.svelte index 986dca90c9..acaf6b9633 100644 --- a/src/lib/components/admin/Users/Groups/EditGroupModal.svelte +++ b/src/lib/components/admin/Users/Groups/EditGroupModal.svelte @@ -49,10 +49,15 @@ tools_export: false }, sharing: { + models: false, public_models: false, + knowledge: false, public_knowledge: false, + prompts: false, public_prompts: false, + tools: false, public_tools: false, + notes: false, public_notes: false }, chat: { diff --git a/src/lib/components/admin/Users/Groups/Permissions.svelte b/src/lib/components/admin/Users/Groups/Permissions.svelte index 35a113e866..9f0e4ef2e9 100644 --- a/src/lib/components/admin/Users/Groups/Permissions.svelte +++ b/src/lib/components/admin/Users/Groups/Permissions.svelte @@ -20,10 +20,15 @@ tools_export: false }, sharing: { + models: false, public_models: false, + knowledge: false, public_knowledge: false, + prompts: false, public_prompts: false, + tools: false, public_tools: false, + notes: false, public_notes: false }, chat: { @@ -217,11 +222,11 @@
- {$i18n.t('Models Public Sharing')} + {$i18n.t('Models Sharing')}
- +
- {#if defaultPermissions?.sharing?.public_models && !permissions.sharing.public_models} + {#if defaultPermissions?.sharing?.models && !permissions.sharing.models}
{$i18n.t('This is a default user permission and will remain enabled.')} @@ -230,14 +235,32 @@ {/if}
+ {#if permissions.sharing.models} +
+
+
+ {$i18n.t('Models Public Sharing')} +
+ +
+ {#if defaultPermissions?.sharing?.public_models && !permissions.sharing.public_models} +
+
+ {$i18n.t('This is a default user permission and will remain enabled.')} +
+
+ {/if} +
+ {/if} +
- {$i18n.t('Knowledge Public Sharing')} + {$i18n.t('Knowledge Sharing')}
- +
- {#if defaultPermissions?.sharing?.public_knowledge && !permissions.sharing.public_knowledge} + {#if defaultPermissions?.sharing?.knowledge && !permissions.sharing.knowledge}
{$i18n.t('This is a default user permission and will remain enabled.')} @@ -246,14 +269,32 @@ {/if}
+ {#if permissions.sharing.knowledge} +
+
+
+ {$i18n.t('Knowledge Public Sharing')} +
+ +
+ {#if defaultPermissions?.sharing?.public_knowledge && !permissions.sharing.public_knowledge} +
+
+ {$i18n.t('This is a default user permission and will remain enabled.')} +
+
+ {/if} +
+ {/if} +
- {$i18n.t('Prompts Public Sharing')} + {$i18n.t('Prompts Sharing')}
- +
- {#if defaultPermissions?.sharing?.public_prompts && !permissions.sharing.public_prompts} + {#if defaultPermissions?.sharing?.prompts && !permissions.sharing.prompts}
{$i18n.t('This is a default user permission and will remain enabled.')} @@ -262,14 +303,32 @@ {/if}
+ {#if permissions.sharing.prompts} +
+
+
+ {$i18n.t('Prompts Public Sharing')} +
+ +
+ {#if defaultPermissions?.sharing?.public_prompts && !permissions.sharing.public_prompts} +
+
+ {$i18n.t('This is a default user permission and will remain enabled.')} +
+
+ {/if} +
+ {/if} +
- {$i18n.t('Tools Public Sharing')} + {$i18n.t('Tools Sharing')}
- +
- {#if defaultPermissions?.sharing?.public_tools && !permissions.sharing.public_tools} + {#if defaultPermissions?.sharing?.tools && !permissions.sharing.tools}
{$i18n.t('This is a default user permission and will remain enabled.')} @@ -278,14 +337,32 @@ {/if}
+ {#if permissions.sharing.tools} +
+
+
+ {$i18n.t('Tools Public Sharing')} +
+ +
+ {#if defaultPermissions?.sharing?.public_tools && !permissions.sharing.public_tools} +
+
+ {$i18n.t('This is a default user permission and will remain enabled.')} +
+
+ {/if} +
+ {/if} +
- {$i18n.t('Notes Public Sharing')} + {$i18n.t('Notes Sharing')}
- +
- {#if defaultPermissions?.sharing?.public_notes && !permissions.sharing.public_notes} + {#if defaultPermissions?.sharing?.notes && !permissions.sharing.notes}
{$i18n.t('This is a default user permission and will remain enabled.')} @@ -293,6 +370,24 @@
{/if}
+ + {#if permissions.sharing.notes} +
+
+
+ {$i18n.t('Notes Public Sharing')} +
+ +
+ {#if defaultPermissions?.sharing?.public_notes && !permissions.sharing.public_notes} +
+
+ {$i18n.t('This is a default user permission and will remain enabled.')} +
+
+ {/if} +
+ {/if}

diff --git a/src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte b/src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte index d6f54af4cd..582c2f68b4 100644 --- a/src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte +++ b/src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte @@ -116,7 +116,8 @@
diff --git a/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte b/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte index 0121c37d0a..dd7d8da8db 100644 --- a/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte +++ b/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte @@ -547,39 +547,41 @@ dragged = false; const handleUploadingFileFolder = (items) => { - for(const item of items) { - - if(item.isFile) { + for (const item of items) { + if (item.isFile) { item.file((file) => { uploadFileHandler(file); - }) + }); continue; } - + // Not sure why you have to call webkitGetAsEntry and isDirectory seperate, but it won't work if you try item.webkitGetAsEntry().isDirectory const wkentry = item.webkitGetAsEntry(); const isDirectory = wkentry.isDirectory; - if(isDirectory) { + if (isDirectory) { // Read the directory - wkentry.createReader().readEntries((entries) => { - handleUploadingFileFolder(entries) - }, (error) => { - console.error('Error reading directory entries:', error); - }); + wkentry.createReader().readEntries( + (entries) => { + handleUploadingFileFolder(entries); + }, + (error) => { + console.error('Error reading directory entries:', error); + } + ); } else { toast.info($i18n.t('Uploading file...')); uploadFileHandler(item.getAsFile()); toast.success($i18n.t('File uploaded!')); } } - } + }; if (e.dataTransfer?.types?.includes('Files')) { if (e.dataTransfer?.files) { const inputItems = e.dataTransfer?.items; if (inputItems && inputItems.length > 0) { - handleUploadingFileFolder(inputItems) + handleUploadingFileFolder(inputItems); } else { toast.error($i18n.t(`File not found.`)); } @@ -708,7 +710,8 @@ { changeDebounceHandler(); }} diff --git a/src/lib/components/workspace/Models/ModelEditor.svelte b/src/lib/components/workspace/Models/ModelEditor.svelte index 012c4d4dbd..df098df85f 100644 --- a/src/lib/components/workspace/Models/ModelEditor.svelte +++ b/src/lib/components/workspace/Models/ModelEditor.svelte @@ -591,7 +591,8 @@
diff --git a/src/lib/components/workspace/Prompts/PromptEditor.svelte b/src/lib/components/workspace/Prompts/PromptEditor.svelte index 21868be80e..f445c5c688 100644 --- a/src/lib/components/workspace/Prompts/PromptEditor.svelte +++ b/src/lib/components/workspace/Prompts/PromptEditor.svelte @@ -83,7 +83,8 @@ bind:show={showAccessControlModal} bind:accessControl accessRoles={['read', 'write']} - allowPublic={$user?.permissions?.sharing?.public_prompts || $user?.role === 'admin'} + share={$user?.permissions?.sharing?.prompts || $user?.role === 'admin'} + sharePublic={$user?.permissions?.sharing?.public_prompts || $user?.role === 'admin'} />
diff --git a/src/lib/components/workspace/Tools/ToolkitEditor.svelte b/src/lib/components/workspace/Tools/ToolkitEditor.svelte index 2bb93d86a4..dd0d4a4a11 100644 --- a/src/lib/components/workspace/Tools/ToolkitEditor.svelte +++ b/src/lib/components/workspace/Tools/ToolkitEditor.svelte @@ -189,7 +189,8 @@ class Tools: bind:show={showAccessControlModal} bind:accessControl accessRoles={['read', 'write']} - allowPublic={$user?.permissions?.sharing?.public_tools || $user?.role === 'admin'} + share={$user?.permissions?.sharing?.tools || $user?.role === 'admin'} + sharePublic={$user?.permissions?.sharing?.public_tools || $user?.role === 'admin'} />
diff --git a/src/lib/components/workspace/common/AccessControl.svelte b/src/lib/components/workspace/common/AccessControl.svelte index 13187bf9cc..89817f964c 100644 --- a/src/lib/components/workspace/common/AccessControl.svelte +++ b/src/lib/components/workspace/common/AccessControl.svelte @@ -15,17 +15,18 @@ export let accessRoles = ['read']; export let accessControl = {}; - export let allowPublic = true; + export let share = true; + export let sharePublic = true; let selectedGroupId = ''; let groups = []; - $: if (!allowPublic && accessControl === null) { + $: if (!sharePublic && accessControl === null) { initPublicAccess(); } const initPublicAccess = () => { - if (!allowPublic && accessControl === null) { + if (!sharePublic && accessControl === null) { accessControl = { read: { group_ids: [], @@ -125,7 +126,7 @@ }} > - {#if allowPublic} + {#if share && sharePublic} {/if} @@ -140,48 +141,50 @@
- {#if accessControl !== null} - {@const accessGroups = groups.filter((group) => - (accessControl?.read?.group_ids ?? []).includes(group.id) - )} -
-
-
-
- {$i18n.t('Groups')} -
-
-
-
-
-
- { - if (selectedGroupId !== '') { - accessControl.read.group_ids = [ - ...(accessControl?.read?.group_ids ?? []), - selectedGroupId - ]; + bind:value={selectedGroupId} + on:change={() => { + if (selectedGroupId !== '') { + accessControl.read.group_ids = [ + ...(accessControl?.read?.group_ids ?? []), + selectedGroupId + ]; - selectedGroupId = ''; - onChange(accessControl); - } - }} - > - - {#each groups.filter((group) => !(accessControl?.read?.group_ids ?? []).includes(group.id)) as group} - - {/each} - -
- +
-
-
+
-
- {#if accessGroups.length > 0} - {#each accessGroups as group} -
-
-
- +
+ {#if accessGroups.length > 0} + {#each accessGroups as group} +
+
+
+ +
+ +
+ {group.name} +
-
- {group.name} -
-
- -
- + }} + > + {#if (accessControl?.write?.group_ids ?? []).includes(group.id)} + + {:else} + + {/if} + - + +
+
+ {/each} + {:else} +
+
+ {$i18n.t('No groups with access, add a group to grant access')}
- {/each} - {:else} -
-
- {$i18n.t('No groups with access, add a group to grant access')} -
-
- {/if} + {/if} +
-
+ {/if} {/if}
diff --git a/src/lib/components/workspace/common/AccessControlModal.svelte b/src/lib/components/workspace/common/AccessControlModal.svelte index 57fe3a2d4d..3e53c8bd04 100644 --- a/src/lib/components/workspace/common/AccessControlModal.svelte +++ b/src/lib/components/workspace/common/AccessControlModal.svelte @@ -9,7 +9,9 @@ export let show = false; export let accessControl = {}; export let accessRoles = ['read']; - export let allowPublic = true; + + export let share = true; + export let sharePublic = true; export let onChange = () => {}; @@ -31,7 +33,7 @@
- +