Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
8f05050
fix private dataset accessible issue
May 7, 2026
533a04a
Fix lint
wangq8 May 8, 2026
835e024
fix: prepend bucket prefix to Azure SPN and SAS storage paths (#14185)
voidborne-d May 7, 2026
879d7a6
Feat: support local provider for code exec component & remove some ou…
Magicbook1108 May 7, 2026
eba562d
Fix: Change route name (#14639)
dcc123456 May 7, 2026
a101192
Perf: push metadata filters down to Elasticsearch (#14576)
sxxtony May 7, 2026
e19b0fc
feat: update Turkish localization strings (#14650)
bakiburakogun May 8, 2026
c9d20a7
fix(go): wire CheckConnection to ListModels in ollama, lm-studio, and…
pandadev66 May 8, 2026
a8bd743
Go: implement Balance in SiliconFlow driver (#14643)
pandadev66 May 8, 2026
c814445
Go: implement provider: OpenRouter (#14652)
Haruko386 May 8, 2026
fb369dd
Go: implement Balance in DeepSeek driver (#14632)
pandadev66 May 8, 2026
d773886
fix(go): implement ListModels and CheckConnection in NVIDIA driver (#…
pandadev66 May 8, 2026
d3b3d4d
fix: add bucket prefix to Azure Blob SPN and SAS storage operations (…
D2758695161 May 8, 2026
e21c2f9
Go: implement Rerank in Gitee AI driver (#14656)
pandadev66 May 8, 2026
225afc2
Fix: display error (#14654)
Lynn-Inf May 8, 2026
6ec2d16
Refa: migrate document preview/download to RESTful API (#14633)
buua436 May 8, 2026
40e73cb
Fix: missing authorization checks in `/files/link-to-datasets` (#14649)
jony376 May 8, 2026
2c165a2
Fix cli login (#14658)
JinHai-CN May 8, 2026
29ddf09
Go: implement remaining interface for OpenRouter (#14657)
Haruko386 May 8, 2026
5e7dc71
Go: implement Encode (embeddings) in Aliyun driver (#14647)
pandadev66 May 8, 2026
011295f
Fix: enforce tenant authorization on document download endpoint (#146…
May 8, 2026
f1c80c7
Fix: collapsible thinking display and separate deep research retrieva…
wanghualoong May 8, 2026
d9dcbc5
Fix: add compatibility route for document download under /v1 (#14663)
buua436 May 8, 2026
2358f9a
Fix: path-aware reset in canvas.run() to preserve cross-run outputs (…
wanghualoong May 8, 2026
4410f9d
Fix: type of tenant_rerank_id (#14667)
Lynn-Inf May 8, 2026
b3ebffb
Go CLI: fix register user (#14665)
JinHai-CN May 8, 2026
0940df9
Fix(Go): prevent global state pollution in local model connection che…
Haruko386 May 8, 2026
eac58f0
Go: fix CLI logout command (#14672)
JinHai-CN May 8, 2026
fe8216c
Fix: handle null document_metadata in kb_prompt to prevent citation c…
May 8, 2026
c31ac43
Do not bypass threshold for rerank when metadata filter is enabled (#…
qinling0210 May 8, 2026
0069515
Fix: move file check (#14681)
Lynn-Inf May 8, 2026
f9d4a35
fix: enforce tenant-scoped authorization for chatbot SDK endpoints (#…
dale053 May 8, 2026
0b16cf2
Refactor : Allow search multiple datasets (#14685)
wangq8 May 8, 2026
b1f15d5
fix test failure issue
May 8, 2026
aaa0f30
Merge branch 'main' into fix/private-datasets-remain-accessible-to-te…
May 8, 2026
1ed5cf9
Merge branch 'main' into fix/private-datasets-remain-accessible-to-te…
May 8, 2026
509c6de
Merge branch 'main' into fix/private-datasets-remain-accessible-to-te…
May 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions api/db/services/document_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -678,17 +678,10 @@ def get_tenant_id_by_name(cls, name):
@classmethod
@DB.connection_context()
def accessible(cls, doc_id, user_id):
docs = (
cls.model.select(cls.model.id)
.join(Knowledgebase, on=(Knowledgebase.id == cls.model.kb_id))
.join(UserTenant, on=(UserTenant.tenant_id == Knowledgebase.tenant_id))
.where(cls.model.id == doc_id, UserTenant.user_id == user_id)
.paginate(0, 1)
)
docs = docs.dicts()
if not docs:
e, doc = cls.get_by_id(doc_id)
if not e:
return False
return True
return KnowledgebaseService.accessible(doc.kb_id, user_id)

@classmethod
@DB.connection_context()
Expand Down
37 changes: 23 additions & 14 deletions api/db/services/knowledgebase_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from peewee import fn, JOIN

from api.db import TenantPermission
from api.db.db_models import DB, Document, Knowledgebase, User, UserTenant, UserCanvas

Check failure on line 21 in api/db/services/knowledgebase_service.py

View workflow job for this annotation

GitHub Actions / ragflow_tests

ruff (F401)

api/db/services/knowledgebase_service.py:21:65: F401 `api.db.db_models.UserTenant` imported but unused help: Remove unused import: `api.db.db_models.UserTenant`
from api.db.services.common_service import CommonService
from common.time_utils import current_timestamp, datetime_format
from api.db.services import duplicate_name
Expand Down Expand Up @@ -485,13 +485,21 @@
# user_id: User ID
# Returns:
# Boolean indicating accessibility
docs = cls.model.select(
cls.model.id).join(UserTenant, on=(UserTenant.tenant_id == Knowledgebase.tenant_id)
).where(cls.model.id == kb_id, UserTenant.user_id == user_id).paginate(0, 1)
docs = docs.dicts()
if not docs:
e, kb = cls.get_by_id(kb_id)
if not e:
return False
return True

if kb.status != StatusEnum.VALID.value:
return False

if kb.tenant_id == user_id:
return True

if kb.permission != TenantPermission.TEAM.value:
return False

joined_tenants = TenantService.get_joined_tenants_by_user_id(user_id)
return any(tenant["tenant_id"] == kb.tenant_id for tenant in joined_tenants)

@classmethod
@DB.connection_context()
Expand All @@ -502,10 +510,10 @@
# user_id: User ID
# Returns:
# List containing dataset information
kbs = cls.model.select().join(UserTenant, on=(UserTenant.tenant_id == Knowledgebase.tenant_id)
).where(cls.model.id == kb_id, UserTenant.user_id == user_id).paginate(0, 1)
kbs = kbs.dicts()
return list(kbs)
e, kb = cls.get_by_id(kb_id)
if not e or not cls.accessible(kb_id, user_id):
return []
return [kb.to_dict()]

@classmethod
@DB.connection_context()
Expand All @@ -516,10 +524,11 @@
# user_id: User ID
# Returns:
# List containing dataset information
kbs = cls.model.select().join(UserTenant, on=(UserTenant.tenant_id == Knowledgebase.tenant_id)
).where(cls.model.name == kb_name, UserTenant.user_id == user_id).paginate(0, 1)
kbs = kbs.dicts()
return list(kbs)
kbs = cls.query(name=kb_name, status=StatusEnum.VALID.value)
for kb in kbs:
if cls.accessible(kb.id, user_id):
return [kb.to_dict()]
return []

@classmethod
@DB.connection_context()
Expand Down
72 changes: 72 additions & 0 deletions test/unit_test/api/db/services/test_dataset_access_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#
# Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from types import SimpleNamespace

from api.db import TenantPermission
from api.db.services.document_service import DocumentService
from api.db.services.knowledgebase_service import KnowledgebaseService
from common.constants import StatusEnum


def _unwrapped_kb_accessible():
return KnowledgebaseService.accessible.__func__.__wrapped__


def _unwrapped_doc_accessible():
return DocumentService.accessible.__func__.__wrapped__


def test_private_dataset_is_not_accessible_to_other_tenant_member(monkeypatch):
kb = SimpleNamespace(
id="kb-private",
tenant_id="owner-1",
permission=TenantPermission.ME.value,
status=StatusEnum.VALID.value,
)

monkeypatch.setattr(KnowledgebaseService, "get_by_id", classmethod(lambda cls, kb_id: (True, kb)))
monkeypatch.setattr(
"api.db.services.knowledgebase_service.TenantService.get_joined_tenants_by_user_id",
lambda _user_id: [{"tenant_id": "owner-1"}],
)

assert _unwrapped_kb_accessible()(KnowledgebaseService, "kb-private", "member-2") is False


def test_team_dataset_is_accessible_to_joined_tenant_member(monkeypatch):
kb = SimpleNamespace(
id="kb-team",
tenant_id="owner-1",
permission=TenantPermission.TEAM.value,
status=StatusEnum.VALID.value,
)

monkeypatch.setattr(KnowledgebaseService, "get_by_id", classmethod(lambda cls, kb_id: (True, kb)))
monkeypatch.setattr(
"api.db.services.knowledgebase_service.TenantService.get_joined_tenants_by_user_id",
lambda _user_id: [{"tenant_id": "owner-1"}],
)

assert _unwrapped_kb_accessible()(KnowledgebaseService, "kb-team", "member-2") is True


def test_document_access_respects_dataset_permission(monkeypatch):
doc = SimpleNamespace(id="doc-1", kb_id="kb-private")

monkeypatch.setattr(DocumentService, "get_by_id", classmethod(lambda cls, doc_id: (True, doc)))
monkeypatch.setattr(KnowledgebaseService, "accessible", classmethod(lambda cls, kb_id, user_id: False))

assert _unwrapped_doc_accessible()(DocumentService, "doc-1", "member-2") is False
Loading