From cdc5084e02c1e29690a0f144deba30ad4c55d6f9 Mon Sep 17 00:00:00 2001 From: arc53-machine <232052973+arc53-machine@users.noreply.github.com> Date: Tue, 29 Sep 2026 00:41:57 +0100 Subject: [PATCH] Refuse a disabled connector before falling back to a legacy tool secret --- docsgpt/connectors/service.py | 5 ++++- tests/connectors/test_service_credentials.py | 15 +++++++++++++++ tests/connectors/test_tool_routes.py | 11 +++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/docsgpt/connectors/service.py b/docsgpt/connectors/service.py index d0e5a454..a33e4ecc 100644 --- a/docsgpt/connectors/service.py +++ b/docsgpt/connectors/service.py @@ -716,9 +716,13 @@ def create_api_key_connection( ``(row, created)``. Raises: + ConnectorDisabled: An admin turned the connector off. Checked first, + so callers that fall back to a legacy path on the other errors + never do so for a disabled connector. ValueError: A required credential field is missing. EncryptionKeyNotConfigured: See :func:`ensure_can_store_credentials`. """ + ensure_connector_allowed(conn, definition.key) fields = {f.key: f for f in definition.credential_fields} if fields: missing = [f.label for f in fields.values() if f.required and not str(credentials.get(f.key) or "").strip()] @@ -728,7 +732,6 @@ def create_api_key_connection( else: credentials = {k: v for k, v in credentials.items() if v not in (None, "")} ensure_can_store_credentials() - ensure_connector_allowed(conn, definition.key) secret_values = {k: v for k, v in credentials.items() if (fields.get(k).secret if fields.get(k) else True)} repo = ConnectorSessionsRepository(conn) existing, account_label = _api_key_account( diff --git a/tests/connectors/test_service_credentials.py b/tests/connectors/test_service_credentials.py index 2f3b1ec9..a3c1954d 100644 --- a/tests/connectors/test_service_credentials.py +++ b/tests/connectors/test_service_credentials.py @@ -300,6 +300,21 @@ class TestApiKeyConnections: pg_conn, "alice", catalog.get_definition("brave"), {"token": "secret-key-1234"}, ) + def test_disabled_connector_refused_before_other_errors(self, pg_conn, monkeypatch): + from docsgpt.connectors import catalog + from docsgpt.core.settings import settings + from docsgpt.security.encryption import DEFAULT_ENCRYPTION_KEY + from docsgpt.storage.db.repositories.connector_policies import ConnectorPoliciesRepository + + monkeypatch.setattr(settings, "ENCRYPTION_SECRET_KEY", DEFAULT_ENCRYPTION_KEY) + monkeypatch.setattr(settings, "AUTH_TYPE", "session_jwt") + ConnectorPoliciesRepository(pg_conn).upsert("telegram", enabled=False) + telegram = catalog.get_definition("telegram") + # Callers fall back to a legacy per-tool secret on these other errors. + for credentials in ({"token": "123456:ABCDEFG"}, {}): + with pytest.raises(service.ConnectorDisabled): + service.create_api_key_connection(pg_conn, "alice", telegram, credentials) + def test_default_key_allowed_single_user(self, pg_conn, monkeypatch): from docsgpt.connectors import catalog from docsgpt.core.settings import settings diff --git a/tests/connectors/test_tool_routes.py b/tests/connectors/test_tool_routes.py index da5d10d8..e5d73d00 100644 --- a/tests/connectors/test_tool_routes.py +++ b/tests/connectors/test_tool_routes.py @@ -136,6 +136,17 @@ class TestCreateConnectedTool: resp = _call(app, CreateTool, _telegram()) assert resp.status_code == 403 + def test_disabled_connector_is_refused_on_the_default_key(self, app, pg_conn): + from docsgpt.api.user.tools.routes import CreateTool + from docsgpt.storage.db.repositories.connector_policies import ConnectorPoliciesRepository + + ConnectorPoliciesRepository(pg_conn).upsert("telegram", enabled=False) + with _db(pg_conn), patch.object(service, "ensure_can_store_credentials", + side_effect=service.EncryptionKeyNotConfigured("set a key")): + resp = _call(app, CreateTool, _telegram()) + assert resp.status_code == 403 + assert pg_conn.execute(text("SELECT count(*) FROM user_tools")).scalar() == 0 + def test_default_key_on_multi_user_falls_back_to_the_tool(self, app, pg_conn): from docsgpt.api.user.tools.routes import CreateTool