Read private repositories only with the user's own GitHub token

GITHUB_ACCESS_TOKEN belongs to the server, but any user could ingest any
repository it can see, private ones included. It is now used only for
public repositories; the loader checks the repository's visibility first
and asks for a GitHub connection otherwise.

The loader also takes a token from the connection a source syncs from.
Merging a connection's keys into a plain repository URL no longer fails
on json.loads, manual Sync now passes the source's connection, and a
token GitHub rejects pauses the connection's sources for reconnect.
This commit is contained in:
arc53-machine committed 2026-09-29 10:41:03 +01:00
1 parent 35d6d80e87
commit 3572d968ec
9 files changed
+357 -22

No files matched your search

+32
View File
@@ -600,6 +600,38 @@ class TestSyncSource:
assert response.status_code == 200
assert response.json["task_id"] == "task-123"
def test_syncs_with_the_sources_connection(self, app, pg_conn):
"""A source synced from a connection (S3, GitHub) reads with that connection's keys."""
from sqlalchemy import text
from docsgpt.api.user.sources.routes import SyncSource
user = "u-conn-sync"
cid = str(pg_conn.execute(text(
"INSERT INTO connector_sessions (user_id, provider, connector_key, auth_kind, status) "
"VALUES (:u, 'github', 'github', 'api_key', 'connected') RETURNING id"
), {"u": user}).scalar())
src = _seed_source(
pg_conn, user, name="repo", type="github",
remote_data=json.dumps({"repo_url": "acme/private"}),
)
pg_conn.execute(text("UPDATE sources SET connection_id = CAST(:c AS uuid) WHERE id = :s"),
{"c": cid, "s": src["id"]})
with _patch_db(pg_conn), patch(
"docsgpt.api.user.sources.routes.sync_source.delay",
return_value=MagicMock(id="task-conn"),
) as mock_delay, app.test_request_context(
"/api/sync_source", method="POST", json={"source_id": str(src["id"])},
):
from flask import request
request.decoded_token = {"sub": user}
response = SyncSource().post()
assert response.status_code == 200
assert mock_delay.call_args.kwargs["connection_id"] == cid
assert mock_delay.call_args.kwargs["source_data"] == "acme/private"
def test_normalizes_dict_remote_data_before_dispatch(self, app, pg_conn):
"""The route must hand the sync task the normalized URL string."""
from docsgpt.api.user.sources.routes import SyncSource
+11 -1
View File
@@ -165,10 +165,20 @@ class TestSyncSourceTask:
)
mock_sync.assert_called_once_with(
ANY, {"data": 1}, "job1", "user1", "web", "daily", "classic", "doc1"
ANY, {"data": 1}, "job1", "user1", "web", "daily", "classic", "doc1",
connection_id=None,
)
assert result == {"status": "ok"}
@pytest.mark.unit
@patch("docsgpt.api.user.tasks.sync")
def test_passes_the_sources_connection(self, mock_sync):
from docsgpt.api.user.tasks import sync_source
sync_source({"data": 1}, "job1", "user1", "s3", "daily", "classic", "doc1", connection_id="c-1")
assert mock_sync.call_args.kwargs["connection_id"] == "c-1"
class TestStoreAttachmentTask:
@pytest.mark.unit