From ac879513e59cf6f349dba7bd6c96c489c56c28ed Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 24 Sep 2025 10:09:59 -0500 Subject: [PATCH] enh: channel read/write perm --- backend/open_webui/models/channels.py | 4 +++ backend/open_webui/routers/channels.py | 31 ++++++++++++++----- backend/open_webui/utils/access_control.py | 6 +++- src/lib/components/channel/Channel.svelte | 5 +++ .../components/channel/MessageInput.svelte | 9 ++++-- src/lib/components/channel/Thread.svelte | 4 +++ .../components/common/RichTextInput.svelte | 11 +++++-- .../layout/Sidebar/ChannelModal.svelte | 2 +- 8 files changed, 58 insertions(+), 14 deletions(-) diff --git a/backend/open_webui/models/channels.py b/backend/open_webui/models/channels.py index 92f238c3a0..e75266be78 100644 --- a/backend/open_webui/models/channels.py +++ b/backend/open_webui/models/channels.py @@ -57,6 +57,10 @@ class ChannelModel(BaseModel): #################### +class ChannelResponse(ChannelModel): + write_access: bool = False + + class ChannelForm(BaseModel): name: str description: Optional[str] = None diff --git a/backend/open_webui/routers/channels.py b/backend/open_webui/routers/channels.py index da52be6e79..17be3b28a6 100644 --- a/backend/open_webui/routers/channels.py +++ b/backend/open_webui/routers/channels.py @@ -10,7 +10,13 @@ from pydantic import BaseModel from open_webui.socket.main import sio, get_user_ids_from_room from open_webui.models.users import Users, UserNameResponse -from open_webui.models.channels import Channels, ChannelModel, ChannelForm +from open_webui.models.groups import Groups +from open_webui.models.channels import ( + Channels, + ChannelModel, + ChannelForm, + ChannelResponse, +) from open_webui.models.messages import ( Messages, MessageModel, @@ -80,7 +86,7 @@ async def create_new_channel(form_data: ChannelForm, user=Depends(get_admin_user ############################ -@router.get("/{id}", response_model=Optional[ChannelModel]) +@router.get("/{id}", response_model=Optional[ChannelResponse]) async def get_channel_by_id(id: str, user=Depends(get_verified_user)): channel = Channels.get_channel_by_id(id) if not channel: @@ -95,7 +101,16 @@ async def get_channel_by_id(id: str, user=Depends(get_verified_user)): status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT() ) - return ChannelModel(**channel.model_dump()) + write_access = has_access( + user.id, type="write", access_control=channel.access_control, strict=False + ) + + return ChannelResponse( + **{ + **channel.model_dump(), + "write_access": write_access or user.role == "admin", + } + ) ############################ @@ -362,7 +377,7 @@ async def new_message_handler( ) if user.role != "admin" and not has_access( - user.id, type="read", access_control=channel.access_control + user.id, type="write", access_control=channel.access_control, strict=False ): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT() @@ -658,7 +673,7 @@ async def add_reaction_to_message( ) if user.role != "admin" and not has_access( - user.id, type="read", access_control=channel.access_control + user.id, type="write", access_control=channel.access_control, strict=False ): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT() @@ -724,7 +739,7 @@ async def remove_reaction_by_id_and_user_id_and_name( ) if user.role != "admin" and not has_access( - user.id, type="read", access_control=channel.access_control + user.id, type="write", access_control=channel.access_control, strict=False ): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT() @@ -806,7 +821,9 @@ async def delete_message_by_id( if ( user.role != "admin" and message.user_id != user.id - and not has_access(user.id, type="read", access_control=channel.access_control) + and not has_access( + user.id, type="write", access_control=channel.access_control, strict=False + ) ): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT() diff --git a/backend/open_webui/utils/access_control.py b/backend/open_webui/utils/access_control.py index 6215a6ac22..af48bebfb4 100644 --- a/backend/open_webui/utils/access_control.py +++ b/backend/open_webui/utils/access_control.py @@ -110,9 +110,13 @@ def has_access( type: str = "write", access_control: Optional[dict] = None, user_group_ids: Optional[Set[str]] = None, + strict: bool = True, ) -> bool: if access_control is None: - return type == "read" + if strict: + return type == "read" + else: + return True if user_group_ids is None: user_groups = Groups.get_groups_by_member_id(user_id) diff --git a/src/lib/components/channel/Channel.svelte b/src/lib/components/channel/Channel.svelte index 53aaefc05b..7502066bdb 100644 --- a/src/lib/components/channel/Channel.svelte +++ b/src/lib/components/channel/Channel.svelte @@ -14,6 +14,7 @@ import Drawer from '../common/Drawer.svelte'; import EllipsisVertical from '../icons/EllipsisVertical.svelte'; import Thread from './Thread.svelte'; + import i18n from '$lib/i18n'; export let id = ''; @@ -252,6 +253,10 @@ {typingUsers} userSuggestions={true} channelSuggestions={true} + disabled={!channel?.write_access} + placeholder={!channel?.write_access + ? $i18n.t('You do not have permission to send messages in this channel.') + : $i18n.t('Type here...')} {onChange} onSubmit={submitHandler} {scrollToBottom} diff --git a/src/lib/components/channel/MessageInput.svelte b/src/lib/components/channel/MessageInput.svelte index 9d2465e29a..0db2e20347 100644 --- a/src/lib/components/channel/MessageInput.svelte +++ b/src/lib/components/channel/MessageInput.svelte @@ -38,7 +38,7 @@ import MentionList from './MessageInput/MentionList.svelte'; import Skeleton from '../chat/Messages/Skeleton.svelte'; - export let placeholder = $i18n.t('Send a Message'); + export let placeholder = $i18n.t('Type here...'); export let id = null; export let chatInputElement; @@ -53,6 +53,7 @@ export let scrollEnd = true; export let scrollToBottom: Function = () => {}; + export let disabled = false; export let acceptFiles = true; export let showFormattingToolbar = true; @@ -731,7 +732,9 @@ -
+
{#if recording} _placeholder }), + Placeholder.configure({ placeholder: () => _placeholder, showOnlyWhenEditable: false }), SelectionDecoration, ...(richText @@ -1123,4 +1123,9 @@
{/if} -
+
diff --git a/src/lib/components/layout/Sidebar/ChannelModal.svelte b/src/lib/components/layout/Sidebar/ChannelModal.svelte index e4ca410ccc..618f7508bf 100644 --- a/src/lib/components/layout/Sidebar/ChannelModal.svelte +++ b/src/lib/components/layout/Sidebar/ChannelModal.svelte @@ -126,7 +126,7 @@
- +