This commit is contained in:
Timothy Jaeryang Baek 2026-10-25 08:33:40 +09:00
parent 0d651a8161
commit f21eadbc1a
3 changed files with 46 additions and 1 deletions

View file

@ -30,6 +30,7 @@ from open_webui.utils.access_control import (
has_public_read_access_grant,
has_public_write_access_grant,
)
from open_webui.utils.access_control.files import has_access_to_note_files
from open_webui.utils.auth import get_admin_user, get_verified_user
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession
@ -533,6 +534,13 @@ async def update_note_by_id(
):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT())
if (
form_data.data
and 'files' in form_data.data
and not await has_access_to_note_files(note, form_data.data['files'], user, db=db)
):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT())
if form_data.access_grants is not None:
form_data.access_grants = await filter_allowed_access_grants(
await Config.get('user.permissions'),

View file

@ -54,6 +54,7 @@ from open_webui.tasks import (
stop_item_tasks,
)
from open_webui.utils.access_control import has_permission
from open_webui.utils.access_control.files import has_access_to_note_files
from open_webui.utils.auth import get_verified_user_by_token
from open_webui.utils.chat_id import is_saved_chat_id
from open_webui.utils.json_codec import SOCKETIO_JSON, JSONCodec, dumps_bytes
@ -1097,6 +1098,13 @@ async def yjs_document_update(sid, data):
log.warning(f'User {user.get("id")} does not have write access to note {note_id}. Rejecting update.')
return
snapshot = data.get('data')
if isinstance(snapshot, dict) and 'files' in snapshot:
editor = await Users.get_user_by_id(user['id'])
if not editor or not await has_access_to_note_files(note, snapshot['files'], editor):
await sio.emit('error', {'message': 'Cannot attach files you cannot access'}, room=sid)
return
update = data.get('update') # List of bytes from frontend
# The document lock makes initialization first-write-wins across workers.

View file

@ -8,7 +8,7 @@ from open_webui.models.folders import FolderModel
from open_webui.models.groups import Groups
from open_webui.models.knowledge import Knowledges
from open_webui.models.models import Models
from open_webui.models.notes import Notes
from open_webui.models.notes import NoteModel, Notes
from open_webui.models.users import UserModel, Users
from sqlalchemy.ext.asyncio import AsyncSession
@ -17,6 +17,35 @@ log = logging.getLogger(__name__)
FOLDER_FILE_TYPES = {'file', 'collection', 'note'}
def get_note_file_ids(entries: list[dict] | None) -> set[str]:
if not isinstance(entries, list):
return set()
return {
entry['id']
for entry in entries
if isinstance(entry, dict) and entry.get('type') == 'file' and isinstance(entry.get('id'), str) and entry['id']
}
async def has_access_to_note_files(
note: NoteModel, entries: list[dict] | None, user: UserModel, db: AsyncSession | None = None
) -> bool:
"""Check the editor's read access to newly attached files."""
if entries is not None and not isinstance(entries, list):
return False
# Existing attachments may belong to another editor; only additions need permission.
added_ids = get_note_file_ids(entries) - get_note_file_ids((note.data or {}).get('files'))
if not added_ids or user.role == 'admin':
return True
group_ids = {group.id for group in await Groups.get_groups_by_member_id(user.id, db=db, include_inherited=True)}
for file_id in added_ids:
if not await has_access_to_file(file_id, 'read', user, db=db, user_group_ids=group_ids):
return False
return True
async def has_access_to_file(
file_id: str | None,
access_type: str,