Skip to content

Commit 259d5ca

Browse files
committed
refac
1 parent 597883a commit 259d5ca

5 files changed

Lines changed: 98 additions & 59 deletions

File tree

backend/open_webui/routers/files.py

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -57,64 +57,7 @@
5757
router = APIRouter()
5858

5959

60-
############################
61-
# Check if the current user has access to a file through any knowledge bases the user may be in.
62-
############################
63-
64-
65-
# TODO: Optimize this function to use the knowledge_file table for faster lookups.
66-
def has_access_to_file(
67-
file_id: Optional[str],
68-
access_type: str,
69-
user=Depends(get_verified_user),
70-
db: Optional[Session] = None,
71-
) -> bool:
72-
file = Files.get_file_by_id(file_id, db=db)
73-
log.debug(f"Checking if user has {access_type} access to file")
74-
if not file:
75-
raise HTTPException(
76-
status_code=status.HTTP_404_NOT_FOUND,
77-
detail=ERROR_MESSAGES.NOT_FOUND,
78-
)
79-
80-
# Check if the file is associated with any knowledge bases the user has access to
81-
knowledge_bases = Knowledges.get_knowledges_by_file_id(file_id, db=db)
82-
user_group_ids = {
83-
group.id for group in Groups.get_groups_by_member_id(user.id, db=db)
84-
}
85-
for knowledge_base in knowledge_bases:
86-
if knowledge_base.user_id == user.id or AccessGrants.has_access(
87-
user_id=user.id,
88-
resource_type="knowledge",
89-
resource_id=knowledge_base.id,
90-
permission=access_type,
91-
user_group_ids=user_group_ids,
92-
db=db,
93-
):
94-
return True
95-
96-
knowledge_base_id = file.meta.get("collection_name") if file.meta else None
97-
if knowledge_base_id:
98-
knowledge_bases = Knowledges.get_knowledge_bases_by_user_id(
99-
user.id, access_type, db=db
100-
)
101-
for knowledge_base in knowledge_bases:
102-
if knowledge_base.id == knowledge_base_id:
103-
return True
104-
105-
# Check if the file is associated with any channels the user has access to
106-
channels = Channels.get_channels_by_file_id_and_user_id(file_id, user.id, db=db)
107-
if access_type == "read" and channels:
108-
return True
109-
110-
# Check if the file is associated with any chats the user has access to
111-
# TODO: Granular access control for chats
112-
chats = Chats.get_shared_chats_by_file_id(file_id, db=db)
113-
if chats:
114-
return True
115-
116-
return False
117-
60+
from open_webui.utils.access_control.files import has_access_to_file
11861

11962
############################
12063
# Upload File

backend/open_webui/routers/retrieval.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from langchain_core.documents import Document
3838

3939
from open_webui.models.files import FileModel, FileUpdateForm, Files
40+
from open_webui.utils.access_control.files import has_access_to_file
4041
from open_webui.models.knowledge import Knowledges
4142
from open_webui.storage.provider import Storage
4243
from open_webui.internal.db import get_session, get_db
@@ -2579,6 +2580,30 @@ async def query_collection_handler(
25792580
form_data: QueryCollectionsForm,
25802581
user=Depends(get_verified_user),
25812582
):
2583+
# Ownership validation: prevent accessing other users' private collections
2584+
if user.role != "admin":
2585+
for collection_name in form_data.collection_names:
2586+
if collection_name.startswith("user-memory-"):
2587+
owner_id = collection_name[len("user-memory-") :]
2588+
if owner_id != user.id:
2589+
raise HTTPException(
2590+
status_code=status.HTTP_403_FORBIDDEN,
2591+
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
2592+
)
2593+
elif collection_name.startswith("file-"):
2594+
file_id = collection_name[len("file-") :]
2595+
file = Files.get_file_by_id(file_id)
2596+
if file and file.user_id != user.id:
2597+
if not has_access_to_file(
2598+
file_id=file_id,
2599+
access_type="read",
2600+
user=user,
2601+
):
2602+
raise HTTPException(
2603+
status_code=status.HTTP_403_FORBIDDEN,
2604+
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
2605+
)
2606+
25822607
try:
25832608
if request.app.state.config.ENABLE_RAG_HYBRID_SEARCH and (
25842609
form_data.hybrid is None or form_data.hybrid

backend/open_webui/tools/builtin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1627,7 +1627,7 @@ async def view_file(
16271627

16281628
try:
16291629
from open_webui.models.files import Files
1630-
from open_webui.routers.files import has_access_to_file
1630+
from open_webui.utils.access_control.files import has_access_to_file
16311631

16321632
user_id = __user__.get("id")
16331633
user_role = __user__.get("role", "user")
File renamed without changes.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import logging
2+
from typing import Optional, Any
3+
4+
from open_webui.models.users import UserModel
5+
from open_webui.models.files import Files
6+
from open_webui.models.knowledge import Knowledges
7+
from open_webui.models.channels import Channels
8+
from open_webui.models.chats import Chats
9+
from open_webui.models.groups import Groups
10+
from open_webui.models.access_grants import AccessGrants
11+
12+
log = logging.getLogger(__name__)
13+
14+
15+
def has_access_to_file(
16+
file_id: Optional[str],
17+
access_type: str,
18+
user: UserModel,
19+
db: Optional[Any] = None,
20+
) -> bool:
21+
"""
22+
Check if a user has the specified access to a file through any of:
23+
- Knowledge bases (ownership or access grants)
24+
- Channels the user is a member of
25+
- Shared chats
26+
27+
NOTE: This does NOT check direct file ownership — callers should check
28+
file.user_id == user.id separately before calling this.
29+
"""
30+
file = Files.get_file_by_id(file_id, db=db)
31+
log.debug(f"Checking if user has {access_type} access to file")
32+
if not file:
33+
return False
34+
35+
# Check if the file is associated with any knowledge bases the user has access to
36+
knowledge_bases = Knowledges.get_knowledges_by_file_id(file_id, db=db)
37+
user_group_ids = {
38+
group.id for group in Groups.get_groups_by_member_id(user.id, db=db)
39+
}
40+
for knowledge_base in knowledge_bases:
41+
if knowledge_base.user_id == user.id or AccessGrants.has_access(
42+
user_id=user.id,
43+
resource_type="knowledge",
44+
resource_id=knowledge_base.id,
45+
permission=access_type,
46+
user_group_ids=user_group_ids,
47+
db=db,
48+
):
49+
return True
50+
51+
knowledge_base_id = file.meta.get("collection_name") if file.meta else None
52+
if knowledge_base_id:
53+
knowledge_bases = Knowledges.get_knowledge_bases_by_user_id(
54+
user.id, access_type, db=db
55+
)
56+
for knowledge_base in knowledge_bases:
57+
if knowledge_base.id == knowledge_base_id:
58+
return True
59+
60+
# Check if the file is associated with any channels the user has access to
61+
channels = Channels.get_channels_by_file_id_and_user_id(file_id, user.id, db=db)
62+
if access_type == "read" and channels:
63+
return True
64+
65+
# Check if the file is associated with any chats the user has access to
66+
# TODO: Granular access control for chats
67+
chats = Chats.get_shared_chats_by_file_id(file_id, db=db)
68+
if chats:
69+
return True
70+
71+
return False

0 commit comments

Comments
 (0)