diff --git a/backend/open_webui/models/models.py b/backend/open_webui/models/models.py index eac1812c98..3239225caf 100755 --- a/backend/open_webui/models/models.py +++ b/backend/open_webui/models/models.py @@ -275,6 +275,24 @@ class ModelsTable: ) return models + async def get_model_owners_attaching_file(self, file_id: str, db: AsyncSession | None = None) -> dict[str, str]: + """Map of model id to owner id for workspace models whose knowledge attaches this file.""" + async with get_async_db_context(db) as db: + # File ids are server-generated uuids, so the text match can only over-match. + result = await db.execute( + select(Model.id, Model.user_id, Model.meta).filter( + Model.base_model_id.is_not(None), cast(Model.meta, String).like(f'%"{file_id}"%') + ) + ) + return { + model_id: user_id + for model_id, user_id, meta in result.all() + if any( + isinstance(item, dict) and item.get('type') == 'file' and item.get('id') == file_id + for item in meta.get('knowledge') or [] + ) + } + @staticmethod def _meta_has_tag(meta: dict | None, tag: str) -> bool: if not meta: @@ -301,23 +319,16 @@ class ModelsTable: for model in all_models ] - async def get_models_by_user_id( - self, - user_id: str, - permission: str = 'write', - db: AsyncSession | None = None, - user_group_ids: set[str] | None = None, - ) -> list[ModelUserResponse]: + async def get_models_by_user_id(self, user_id: str, db: AsyncSession | None = None) -> list[ModelUserResponse]: models = await self.get_models(db=db) - if user_group_ids is None: - user_group_ids = {group.id for group in await Groups.get_groups_by_member_id(user_id, db=db)} + user_group_ids = {group.id for group in await Groups.get_groups_by_member_id(user_id, db=db)} # One grants query for all non-owned models instead of one per model accessible_ids = await AccessGrants.get_accessible_resource_ids( user_id=user_id, resource_type='model', resource_ids=[model.id for model in models if model.user_id != user_id], - permission=permission, + permission='write', user_group_ids=user_group_ids, db=db, ) diff --git a/backend/open_webui/utils/access_control/files.py b/backend/open_webui/utils/access_control/files.py index e3bbaf8fbd..5d192db603 100644 --- a/backend/open_webui/utils/access_control/files.py +++ b/backend/open_webui/utils/access_control/files.py @@ -106,16 +106,22 @@ async def has_access_to_file( # Check if the file is directly attached to a shared workspace model (per the ownership # note above, model write is conferred only for files the model owner owns). - for model in await Models.get_models_by_user_id( - user.id, permission=access_type, db=db, user_group_ids=user_group_ids - ): - knowledge_items = getattr(model.meta, 'knowledge', None) or [] - for item in knowledge_items: - if isinstance(item, dict) and item.get('type') == 'file' and item.get('id') == file.id: - if access_type == 'read' or model.user_id == file.user_id: - return True + model_owners = await Models.get_model_owners_attaching_file(file.id, db=db) + if access_type != 'read': + model_owners = {model_id: owner_id for model_id, owner_id in model_owners.items() if owner_id == file.user_id} + if user.id in model_owners.values(): + return True - return False + return bool( + await AccessGrants.get_accessible_resource_ids( + user_id=user.id, + resource_type='model', + resource_ids=list(model_owners), + permission=access_type, + user_group_ids=user_group_ids, + db=db, + ) + ) async def get_accessible_folder_files(