mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-15 08:16:38 +00:00
Merge main (roles and access revamp) into connectors
Main's access model (team editors and viewers, edit_credentials, owner-only OAuth servers, per-user tool preferences, resource sponsors) now applies to connection-backed tools and sources. Our migrations are renumbered to 0040_connections and 0041_connection_account_name, after main's 0038_resource_access_settings and 0039_resource_sponsors. Where the two sides met: MCP tools save and load their connections as the tool owner, editors may add a key (a new connection on the owner's account) but never rewrite an existing connection's secret, fixed values stay owner-only, and a member's own connection is used in member mode.
This commit is contained in:
commit
a1789d2ab4
167 files changed
+16909
-2126
No files matched your search
@@ -152,6 +152,17 @@ def patched_wiki(monkeypatch, reembed_mock, rebuild_mock):
|
||||
task = MagicMock()
|
||||
task.delay = reembed_mock
|
||||
monkeypatch.setattr("docsgpt.api.user.tasks.reembed_wiki_page", task)
|
||||
# Every write re-checks the caller's live grant; default to editor.
|
||||
monkeypatch.setattr(
|
||||
"docsgpt.api.user.resource_access.resolve",
|
||||
lambda conn, rt, rid, uid: _access("editor"),
|
||||
)
|
||||
|
||||
|
||||
def _access(level):
|
||||
from docsgpt.api.user.resource_access import build
|
||||
|
||||
return build("source", "src-1", level, "owner-sub", {})
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -194,6 +205,54 @@ class TestBasics:
|
||||
assert wiki_tool.updated_by == "caller-sub"
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# Live grant re-check on writes
|
||||
# =====================================================================
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestLiveWriteCheck:
|
||||
@pytest.mark.parametrize("revoked", [None, "viewer"])
|
||||
def test_revoked_or_viewer_cannot_write(self, wiki_tool, monkeypatch, reembed_mock, revoked):
|
||||
wiki_tool.execute_action("create", path="/a.md", content="one")
|
||||
monkeypatch.setattr(
|
||||
"docsgpt.api.user.resource_access.resolve",
|
||||
lambda conn, rt, rid, uid: _access(revoked) if revoked else None,
|
||||
)
|
||||
reembed_mock.reset_mock()
|
||||
for action, kwargs in (
|
||||
("create", {"path": "/b.md", "content": "x"}),
|
||||
("str_replace", {"path": "/a.md", "old_str": "one", "new_str": "two"}),
|
||||
("insert", {"path": "/a.md", "insert_line": 1, "insert_text": "x"}),
|
||||
("delete", {"path": "/a.md"}),
|
||||
("rename", {"old_path": "/a.md", "new_path": "/c.md"}),
|
||||
):
|
||||
result = wiki_tool.execute_action(action, **kwargs)
|
||||
assert "no longer have edit access" in result, action
|
||||
reembed_mock.assert_not_called()
|
||||
# Reads still work.
|
||||
assert "one" in wiki_tool.execute_action("view", path="/a.md")
|
||||
|
||||
def test_checks_the_invoking_user(self, wiki_tool, monkeypatch):
|
||||
seen = []
|
||||
|
||||
def _resolve(conn, rt, rid, uid):
|
||||
seen.append((rt, rid, uid))
|
||||
return _access("editor")
|
||||
|
||||
monkeypatch.setattr("docsgpt.api.user.resource_access.resolve", _resolve)
|
||||
wiki_tool.execute_action("create", path="/a.md", content="x")
|
||||
assert seen == [("source", "src-1", "caller-sub")]
|
||||
|
||||
def test_no_caller_denied(self, patched_wiki):
|
||||
from docsgpt.agents.tools.wiki import WikiTool
|
||||
|
||||
tool = WikiTool({"source_id": "src-1", "source_owner_id": "owner-sub"})
|
||||
assert "no longer have edit access" in tool.execute_action(
|
||||
"create", path="/a.md", content="x"
|
||||
)
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# create / view
|
||||
# =====================================================================
|
||||
@@ -498,8 +557,8 @@ class TestBuildAgentGating:
|
||||
_noop_conn,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"docsgpt.api.user.team_sharing.effective_write_owner",
|
||||
lambda conn, rt, rid, uid: "owner-x",
|
||||
"docsgpt.api.answer.services.stream_processor._wiki_write_owner",
|
||||
lambda conn, sid, uid: "owner-x",
|
||||
)
|
||||
cfg = proc._build_wiki_config()
|
||||
assert cfg is not None
|
||||
@@ -524,10 +583,10 @@ class TestBuildAgentGating:
|
||||
"docsgpt.api.answer.services.stream_processor.db_readonly",
|
||||
_noop_conn,
|
||||
)
|
||||
# Viewer: effective_write_owner returns None.
|
||||
# Viewer: no ``edit`` on the source, so no write owner.
|
||||
monkeypatch.setattr(
|
||||
"docsgpt.api.user.team_sharing.effective_write_owner",
|
||||
lambda conn, rt, rid, uid: None,
|
||||
"docsgpt.api.answer.services.stream_processor._wiki_write_owner",
|
||||
lambda conn, sid, uid: None,
|
||||
)
|
||||
assert proc._build_wiki_config() is None
|
||||
|
||||
@@ -550,8 +609,8 @@ class TestBuildAgentGating:
|
||||
_noop_conn,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"docsgpt.api.user.team_sharing.effective_write_owner",
|
||||
lambda conn, rt, rid, uid: "owner-x",
|
||||
"docsgpt.api.answer.services.stream_processor._wiki_write_owner",
|
||||
lambda conn, sid, uid: "owner-x",
|
||||
)
|
||||
assert proc._build_wiki_config() is None
|
||||
|
||||
@@ -574,8 +633,8 @@ class TestBuildAgentGating:
|
||||
_noop_conn,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"docsgpt.api.user.team_sharing.effective_write_owner",
|
||||
lambda conn, rt, rid, uid: "owner-x",
|
||||
"docsgpt.api.answer.services.stream_processor._wiki_write_owner",
|
||||
lambda conn, sid, uid: "owner-x",
|
||||
)
|
||||
cfg = proc._build_wiki_config()
|
||||
assert cfg is not None
|
||||
|
||||
Reference in new issue
Block a user