mirror of
https://github.com/volcengine/OpenViking.git
synced 2026-09-28 19:53:23 +08:00
* feat(acl): add resource access control Persist direct and inherited ACL fields in context records so filesystem operations and vector retrieval enforce the same permissions. * docs(acl): align behavior documentation * feat(acl): 支持用户组授权 * refactor(acl): 收敛权限更新并补齐异步身份 确保 ACL 更新结果与锁内状态一致,移动失败可恢复向量 URI,并让后台解析沿用发起请求的用户组身份。精简重复实现和低价值测试。 * refactor(acl): 仅在共享资源区启用授权 个人资源保持 owner 私有,分享通过移动到共享区完成;跨区移动按目标 scope 继承或清空 ACL,并将向量权限过滤限制在共享区。 * chore(acl): 移除无关解析器清理 恢复 telemetry 空上下文和 import 的基线写法,仅保留 ACL 用户组身份传递。 * fix(acl): 收紧权限读取与过滤分页 ACL 元数据读取失败时终止权限判断,并确保 grep 的 node_limit 作用于权限过滤后的可见结果。 * refactor(acl): 收敛测试与检索取数逻辑 删除重复、时序绑定和实现细节测试,并撤销非必要的 grep 循环扩容。保留用例只覆盖独立的安全、一致性和兼容性故障。 * refactor(acl): 统一资源操作鉴权 将 write、delete 和 reindex 收敛到 read/write/manage 能力检查,并修复 add target 权限校验未等待的问题。 * refactor(acl): 枚举化权限能力 用 AclAction 和 AclLevel 约束内部权限分支,拒绝裸字符串 action,并保持 API 与存储字符串格式兼容。 * fix(acl): route reindex through unified authorization * refactor(uri): remove redundant user content root helper * fix(acl): align resource browsing and ingestion permissions * test(acl): consolidate ACL contract coverage * feat(acl): grant creators manager access * fix(acl): scope creator grants to controlled trees * fix(acl): isolate add-resource parent refresh authorization * feat(acl): enforce snapshot permissions * fix(acl): preserve content write permission errors * feat(acl): add opt-in protection for new shared content * feat(cli): configure automatic ACL protection * refactor(acl): simplify authorization flow * fix(acl): allow write permission for file move and delete * refactor(acl): simplify group and permission semantics * fix(acl): allow write access for snapshot file deletion * fix(acl): preserve user-scoped reindex access * refactor(acl): compact indexed principal grants Store one highest-permission token per principal so in-memory authorization and vector filtering share the same ACL representation. * fix(acl): preserve background task identity * fix(acl): bypass acl for watch refresh
95 lines
3.6 KiB
Python
95 lines
3.6 KiB
Python
# Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd.
|
|
# SPDX-License-Identifier: AGPL-3.0
|
|
"""CLI content operation tests (read, abstract, overview, download, write, reindex)."""
|
|
|
|
import os
|
|
import tempfile
|
|
import uuid
|
|
|
|
import pytest
|
|
from conftest import ov, ov_add_resource, ov_reindex, ov_rm, ov_write
|
|
|
|
pytestmark = pytest.mark.cli_remote
|
|
|
|
|
|
class TestContentRead:
|
|
def test_read(self, test_file_uri):
|
|
r = ov(["read", test_file_uri, "-o", "json"])
|
|
assert r["exit_code"] == 0, (
|
|
f"ov read should exit 0, got {r['exit_code']}: {r['stderr'][:300]}"
|
|
)
|
|
assert len(r["stdout"]) > 0, "read output should not be empty"
|
|
assert "CLI Test" in r["stdout"] or "test" in r["stdout"].lower(), (
|
|
f"read output should contain test content, got: {r['stdout'][:200]}"
|
|
)
|
|
|
|
|
|
class TestContentAbstract:
|
|
def test_abstract(self, test_pack_uri):
|
|
r = ov(["abstract", test_pack_uri, "-o", "json"])
|
|
assert r["exit_code"] == 0, (
|
|
f"ov abstract should exit 0, got {r['exit_code']}: {r['stderr'][:300]}"
|
|
)
|
|
assert len(r["stdout"]) > 0, "abstract output should not be empty"
|
|
|
|
|
|
class TestContentOverview:
|
|
def test_overview(self, test_pack_uri):
|
|
r = ov(["overview", test_pack_uri, "-o", "json"])
|
|
assert r["exit_code"] == 0, (
|
|
f"ov overview should exit 0, got {r['exit_code']}: {r['stderr'][:300]}"
|
|
)
|
|
assert len(r["stdout"]) > 0, "overview output should not be empty"
|
|
|
|
|
|
class TestContentDownload:
|
|
def test_get_download(self, test_file_uri, tmp_path):
|
|
local_path = str(tmp_path / "downloaded.txt")
|
|
r = ov(["get", test_file_uri, local_path, "-o", "json"])
|
|
assert r["exit_code"] == 0, (
|
|
f"ov get should exit 0, got {r['exit_code']}: {r['stderr'][:300]}"
|
|
)
|
|
assert os.path.exists(local_path), "downloaded file should exist"
|
|
assert os.path.getsize(local_path) > 0, "downloaded file should not be empty"
|
|
|
|
|
|
class TestContentWrite:
|
|
def test_write_replace(self, test_file_uri):
|
|
r = ov_write(test_file_uri, "Updated via CLI write.", "--mode", "replace")
|
|
assert r["exit_code"] == 0, (
|
|
f"ov write replace should exit 0, got {r['exit_code']}: {r['stderr'][:300]}"
|
|
)
|
|
data = r["json"]
|
|
assert data is not None and data.get("ok") is True, "Expected ok=true"
|
|
|
|
def test_write_append(self, test_file_uri):
|
|
r = ov_write(test_file_uri, "\nAppended via CLI.", "--append")
|
|
assert r["exit_code"] == 0, (
|
|
f"ov write append should exit 0, got {r['exit_code']}: {r['stderr'][:300]}"
|
|
)
|
|
data = r["json"]
|
|
assert data is not None and data.get("ok") is True, "Expected ok=true"
|
|
|
|
|
|
class TestContentReindex:
|
|
def test_reindex(self):
|
|
reindex_pack = f"viking://~/resources/reindex_{uuid.uuid4().hex[:6]}"
|
|
with tempfile.NamedTemporaryFile(suffix=".txt", delete=False, mode="w") as f:
|
|
f.write("# Reindex Test\n\nThis is an independent resource for reindex testing.")
|
|
temp_path = f.name
|
|
try:
|
|
try:
|
|
r = ov_add_resource(temp_path, reindex_pack)
|
|
assert r["exit_code"] == 0, f"add-resource for reindex failed: {r['stderr'][:300]}"
|
|
finally:
|
|
os.unlink(temp_path)
|
|
|
|
r = ov_reindex(reindex_pack)
|
|
assert r["exit_code"] == 0, (
|
|
f"ov reindex should exit 0, got {r['exit_code']}: {r['stderr'][:300]}"
|
|
)
|
|
data = r["json"]
|
|
assert data is not None and data.get("ok") is True, "Expected ok=true"
|
|
finally:
|
|
ov_rm(reindex_pack)
|