fix: address PR #813 latest review (ACL-aware doc-type discovery, robustness)
- get_indexed_doc_types: add optional accessible_owners param and reuse
build_ownership_filter so cross-user doc-type discovery matches the real
search scope (was self-only / ACL-blind); docstring documents the self-only
default. Covered by test_get_indexed_doc_types_is_acl_aware.
- access_filter: build_ownership_filter now omits the owner_id branch entirely
for an empty owner set instead of relying on undocumented MatchAny(any=[])
semantics; updated the empty-list unit test accordingly.
- access_filter: make the uid_owner/owner share-owner extraction explicit
("absent, not empty") to avoid skipping on a falsy-but-present field.
- access_filter: add an operator note that pre-owner_id points need a re-index
to surface to share recipients (ACL search is a no-op for legacy data).
- verification/webdav: lock the file_accessible_by_id(scope="") contract with a
targeted multi-user test (owner + recipient True, non-recipient False).
- viz_routes: comment that verify-on-read eviction runs inline by design (no
lifespan task group available on the Starlette route).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
531228d407
commit
423d0a1758
@@ -23,6 +23,7 @@ from qdrant_client.models import Distance, PointStruct, VectorParams
|
||||
|
||||
from nextcloud_mcp_server.config import get_settings
|
||||
from nextcloud_mcp_server.embedding import SimpleEmbeddingProvider
|
||||
from nextcloud_mcp_server.search.algorithms import get_indexed_doc_types
|
||||
from nextcloud_mcp_server.search.semantic import SemanticSearchAlgorithm
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
@@ -92,6 +93,11 @@ async def seeded_collection(monkeypatch):
|
||||
"nextcloud_mcp_server.search.semantic.get_embedding_service",
|
||||
lambda: provider,
|
||||
)
|
||||
# get_indexed_doc_types reads the client from the algorithms module.
|
||||
monkeypatch.setattr(
|
||||
"nextcloud_mcp_server.search.algorithms.get_qdrant_client",
|
||||
AsyncMock(return_value=client),
|
||||
)
|
||||
|
||||
yield provider
|
||||
|
||||
@@ -170,3 +176,16 @@ async def test_owner_sees_own_new_style_point(seeded_collection):
|
||||
assert "101" in found
|
||||
assert "102" not in found
|
||||
assert "103" not in found
|
||||
|
||||
|
||||
async def test_get_indexed_doc_types_is_acl_aware(seeded_collection):
|
||||
"""get_indexed_doc_types respects the ownership scope: with the expanded
|
||||
accessible_owners Bob discovers the shared "file" type, but self-only Bob
|
||||
(who owns nothing here) discovers nothing — proving it is no longer
|
||||
ACL-blind."""
|
||||
# ACL-aware: Bob can read Alice's shared file → discovers "file".
|
||||
assert await get_indexed_doc_types("bob", accessible_owners=["bob", "alice"]) == {
|
||||
"file"
|
||||
}
|
||||
# Self-only (default): Bob owns nothing here → discovers nothing.
|
||||
assert await get_indexed_doc_types("bob") == set()
|
||||
|
||||
@@ -195,3 +195,23 @@ async def test_non_recipient_does_not_find_file(acl_users, seeded_semantic):
|
||||
kept = await _search_as(acl_users["diana"], seeded_semantic)
|
||||
|
||||
assert kept == [], "diana (no share) must not find alice's file"
|
||||
|
||||
|
||||
async def test_file_accessible_by_id_resolves_shares(acl_users, shared_file):
|
||||
"""Lock the verify-on-read contract directly on ``file_accessible_by_id``.
|
||||
|
||||
The WebDAV SEARCH-by-fileid with ``scope=""`` must resolve a file that the
|
||||
caller does NOT own but which is shared with them. This is the exact check
|
||||
verify-on-read depends on for shared, nested files; a Nextcloud change to
|
||||
how ``scope=""`` is interpreted would otherwise silently break ACL-aware
|
||||
verification. The file lives in a subfolder, so a path-based check would
|
||||
404 for the recipient — only the by-id SEARCH gets it right.
|
||||
"""
|
||||
file_id, _path = shared_file
|
||||
fid = int(file_id)
|
||||
|
||||
# Owner and share recipient can both reach it...
|
||||
assert await acl_users["alice"].webdav.file_accessible_by_id(fid) is True
|
||||
assert await acl_users["bob"].webdav.file_accessible_by_id(fid) is True
|
||||
# ...the non-recipient cannot.
|
||||
assert await acl_users["diana"].webdav.file_accessible_by_id(fid) is False
|
||||
|
||||
Reference in New Issue
Block a user