From dd29f50cd97414bee56671c8abb8e398e979e4e1 Mon Sep 17 00:00:00 2001 From: Jan-Kazlouski-elastic Date: Wed, 7 Oct 2026 17:29:55 +0300 Subject: [PATCH] chore(cli): stop creating connector secrets for native connectors (#4579) ## Closes https://github.com/elastic/connectors/issues/2990 Follow-up to #4524. Since 9.0 (#2989) the framework never reads connector secrets, but `connectors connector create --native` still stored the connector API key in a connector secret and wrote its id to `api_key_secret_id`. Nothing consumes that secret. Changes: - CLI no longer creates a connector secret; `api_key_secret_id` is written as `null` - Remove `ESManagementClient.create_connector_secret()` (CLI was the only caller) and its test - Remove `test_get_index_or_alias`, a copy of the secret test that never called `get_index_or_alias` (covered by `test_extract_index_or_alias_*`) - `CONNECTOR_PROTOCOL.md`: note `api_key_secret_id` is not read by the framework since 9.0 Deliberately kept: - `native_connector_api_keys` in `BaseDataSource.features()`: part of the ES `ConnectorFeatures` schema; removing it would make native connector docs be rewritten on every scheduler poll, because the key sets would never match - `api_key_secret_id` field: still part of the ES Connector API and written by Kibana Not for 8.19: 8.19 still reads connector secrets during native syncs. ## Checklists #### Pre-Review Checklist - [x] this PR does NOT contain credentials of any kind, such as API keys or username/passwords (double check `config.yml.example`) - [x] this PR has a meaningful title - [x] this PR links to all relevant github issues that it fixes or partially addresses - [x] Covered the changes with automated tests - [x] Tested the changes locally (`make clean install autoformat lint test`) - [x] Added a label for each target release version (example: `v7.13.2`, `v7.14.0`, `v8.0.0`) ## Release Note The `connectors connector create --native` CLI command no longer stores the connector API key in a connector secret. --- .../connectors/cli/connector.py | 8 +-- .../connectors/es/management_client.py | 15 ----- .../tests/es/test_management_client.py | 36 ----------- .../tests/test_connectors_cli.py | 61 +++++++++---------- docs/CONNECTOR_PROTOCOL.md | 2 +- 5 files changed, 30 insertions(+), 92 deletions(-) diff --git a/app/connectors_service/connectors/cli/connector.py b/app/connectors_service/connectors/cli/connector.py index 1a446695d..2a07496f6 100644 --- a/app/connectors_service/connectors/cli/connector.py +++ b/app/connectors_service/connectors/cli/connector.py @@ -123,7 +123,6 @@ async def __create_connector( await self.cli_client.create_content_index(index_name, language) api_key_id = None - api_key_secret_id = None api_key_encoded = None api_key_error = None api_key_skipped = False @@ -136,8 +135,6 @@ async def __create_connector( api_key = await self.__create_api_key(index_name) api_key_id = api_key["id"] api_key_encoded = api_key["encoded"] - if is_native: - api_key_secret_id = await self.__store_api_key(api_key_encoded) except Exception as e: api_key_error = f"Could not create a connector-specific API key. Elasticsearch reported the following error {e}" @@ -145,7 +142,7 @@ async def __create_connector( # TODO features still required doc = { "api_key_id": api_key_id, - "api_key_secret_id": api_key_secret_id, + "api_key_secret_id": None, "configuration": configuration, "index_name": index_name, "name": name, @@ -264,6 +261,3 @@ async def __create_api_key(self, name): role_descriptors=role_descriptors, metadata=metadata, ) - - async def __store_api_key(self, encoded_api_key): - return await self.cli_client.create_connector_secret(encoded_api_key) diff --git a/app/connectors_service/connectors/es/management_client.py b/app/connectors_service/connectors/es/management_client.py index 1de30a99c..c5600caef 100644 --- a/app/connectors_service/connectors/es/management_client.py +++ b/app/connectors_service/connectors/es/management_client.py @@ -210,18 +210,3 @@ async def yield_existing_documents_metadata(self, index): timestamp = source.get(TIMESTAMP_FIELD) yield doc_id, timestamp - - async def create_connector_secret(self, secret_value): - secret = await self._retrier.execute_with_retry( - partial( - self.client.perform_request, - "POST", - "/_connector/_secret", - headers={ - "accept": "application/json", - "content-type": "application/json", - }, - body={"value": secret_value}, - ) - ) - return secret.get("id") diff --git a/app/connectors_service/tests/es/test_management_client.py b/app/connectors_service/tests/es/test_management_client.py index 53c6c4913..e2a95dda5 100644 --- a/app/connectors_service/tests/es/test_management_client.py +++ b/app/connectors_service/tests/es/test_management_client.py @@ -216,24 +216,6 @@ async def test_yield_existing_documents_metadata_when_source_id_differs_from_id( assert ids == ["1", "2"] - @pytest.mark.asyncio - async def test_create_connector_secret(self, es_management_client, mock_responses): - secret_id = "secret-id" - secret_value = "my-secret" - - es_management_client.client.perform_request = AsyncMock( - return_value={"id": secret_id} - ) - - returned_id = await es_management_client.create_connector_secret(secret_value) - assert returned_id == secret_id - es_management_client.client.perform_request.assert_awaited_with( - "POST", - "/_connector/_secret", - body={"value": secret_value}, - headers={"accept": "application/json", "content-type": "application/json"}, - ) - @pytest.mark.asyncio async def test_extract_index_or_alias_with_index(self, es_management_client): response = { @@ -281,21 +263,3 @@ async def test_extract_index_or_alias_when_none_present(self, es_management_clie index = await es_management_client.get_index_or_alias("nonexistent") assert index is None - - @pytest.mark.asyncio - async def test_get_index_or_alias(self, es_management_client, mock_responses): - secret_id = "secret-id" - secret_value = "my-secret" - - es_management_client.client.perform_request = AsyncMock( - return_value={"id": secret_id} - ) - - returned_id = await es_management_client.create_connector_secret(secret_value) - assert returned_id == secret_id - es_management_client.client.perform_request.assert_awaited_with( - "POST", - "/_connector/_secret", - body={"value": secret_value}, - headers={"accept": "application/json", "content-type": "application/json"}, - ) diff --git a/app/connectors_service/tests/test_connectors_cli.py b/app/connectors_service/tests/test_connectors_cli.py index 7d0178393..22e815c35 100644 --- a/app/connectors_service/tests/test_connectors_cli.py +++ b/app/connectors_service/tests/test_connectors_cli.py @@ -528,43 +528,38 @@ def test_connector_create_native_connector(patched_confirm): AsyncMock(return_value={"id": "api-key-123", "encoded": "foo"}), ) as patched_create_api_key: with patch( - "connectors.cli.connector.Connector._Connector__store_api_key", - AsyncMock(return_value="secret-123"), - ) as patched_store_api_key: - with patch( - "connectors.protocol.connectors.ConnectorIndex.index", - AsyncMock(return_value={"_id": "new_connector_id"}), - ) as patched_create: - result = runner.invoke( - cli, - [ - "connector", - "create", - "--service-type", - "mongodb", - "--from-index", - "--native", - ], - input=_mongodb_create_cli_input(index_name="test-connector"), - ) + "connectors.protocol.connectors.ConnectorIndex.index", + AsyncMock(return_value={"_id": "new_connector_id"}), + ) as patched_create: + result = runner.invoke( + cli, + [ + "connector", + "create", + "--service-type", + "mongodb", + "--from-index", + "--native", + ], + input=_mongodb_create_cli_input(index_name="test-connector"), + ) - patched_create.assert_called_once() + patched_create.assert_called_once() - call_args = patched_create.call_args[0][0] - assert call_args["is_native"] is True - assert call_args["api_key_id"] == "api-key-123" - assert call_args["api_key_secret_id"] == "secret-123" - _assert_mongodb_create_doc( - call_args, - index_name="test-connector", - connector_name=_DEFAULT_CREATE_OPTIONS["connector_name"], - ) + call_args = patched_create.call_args[0][0] + assert call_args["is_native"] is True + assert call_args["api_key_id"] == "api-key-123" + assert call_args["api_key_secret_id"] is None + _assert_mongodb_create_doc( + call_args, + index_name="test-connector", + connector_name=_DEFAULT_CREATE_OPTIONS["connector_name"], + ) - patched_create_api_key.assert_called_once() - patched_store_api_key.assert_called_once() - assert result.exit_code == 0 + patched_create_api_key.assert_called_once() + assert result.exit_code == 0 - assert "has been created" in result.output + assert "has been created" in result.output def test_index_help_page(): diff --git a/docs/CONNECTOR_PROTOCOL.md b/docs/CONNECTOR_PROTOCOL.md index 2c063d7ca..5f26882f5 100644 --- a/docs/CONNECTOR_PROTOCOL.md +++ b/docs/CONNECTOR_PROTOCOL.md @@ -53,7 +53,7 @@ This is our main communication index, used to communicate the connector's config ``` { api_key_id: string; -> ID of the current API key in use - api_key_secret_id: string; -> ID of Connector Secret doc that stores the API key + api_key_secret_id: string; -> ID of Connector Secret doc that stores the API key (not read by the connectors framework since 9.0) configuration: { [key]: { default_value: any; -> The value used if `value` is empty (only for non-required fields)