This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 4eb6170143d [api] Add logic redacted sensitive fields via the Public 
API and UI (#59873)
4eb6170143d is described below

commit 4eb6170143d566920b8db508fc1fa50a9db18755
Author: nhuantho <[email protected]>
AuthorDate: Tue Jan 6 06:41:47 2026 +0700

    [api] Add logic redacted sensitive fields via the Public API and UI (#59873)
    
    * Marked sensitive value in config API
    
    * Ignore tuple type
    
    * Marked sensitive value in Connection UI API
    
    * Fix mypy
    
    * Fix ruff check
    
    * fix redact_value of ConfigOption
    
    * Fix mypy
    
    * Add a test with dict value inclue sensitive and normal fields
    
    * Fix logic after pr 59880
    
    * Remove print in test_connections.py
    
    ---------
    
    Co-authored-by: nhuan.bc <[email protected]>
    Co-authored-by: Jason(Zhe-You) Liu 
<[email protected]>
---
 .../api_fastapi/core_api/datamodels/connections.py |  8 ++++
 .../core_api/routes/ui/test_connections.py         | 50 ++++++++++++++++++++++
 2 files changed, 58 insertions(+)

diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/connections.py 
b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/connections.py
index ee22c09d116..dcb8be63a87 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/connections.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/connections.py
@@ -123,6 +123,14 @@ class ConnectionHookMetaData(BaseModel):
     standard_fields: StandardHookFields | None
     extra_fields: Mapping | None
 
+    @field_validator("extra_fields", mode="after")
+    @classmethod
+    def redact_extra_fields(cls, v: Mapping | None):
+        if v is None:
+            return None
+
+        return redact(v)
+
 
 # Request Models
 class ConnectionBody(StrictBaseModel):
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_connections.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_connections.py
index 52b624108eb..f354e172137 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_connections.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_connections.py
@@ -16,8 +16,16 @@
 # under the License.
 from __future__ import annotations
 
+from unittest import mock
+
 import pytest
 
+from airflow.api_fastapi.core_api.datamodels.connections import (
+    ConnectionHookFieldBehavior,
+    ConnectionHookMetaData,
+    StandardHookFields,
+)
+
 from tests_common.test_utils.asserts import assert_queries_count
 from tests_common.test_utils.markers import 
skip_if_force_lowest_dependencies_marker
 
@@ -37,6 +45,48 @@ class TestHookMetaData:
             if hook_data["connection_type"] == "fs":
                 assert hook_data["hook_name"] == "File (path)"
 
+    @pytest.mark.parametrize(
+        ("extra_fields", "expected_response"),
+        [
+            ({"secret_key": "test-secret_key"}, {"secret_key": "***"}),
+            ({"extra_fields": "test-extra_fields"}, {"extra_fields": 
"test-extra_fields"}),
+            (
+                {
+                    "secret_key": "test-secret_key",
+                    "extra_fields": "test-extra_fields",
+                    "password": "test-password",
+                },
+                {"secret_key": "***", "extra_fields": "test-extra_fields", 
"password": "***"},
+            ),
+        ],
+    )
+    @pytest.mark.enable_redact
+    
@mock.patch("airflow.api_fastapi.core_api.routes.ui.connections.HookMetaService")
+    def test_get_should_respond_200_with_extra_fields(
+        self, hook_meta_service, test_client, extra_fields, expected_response
+    ):
+        hook_meta_service.hook_meta_data.return_value = [
+            ConnectionHookMetaData(
+                connection_type="smtp",
+                hook_class_name="airflow.providers.sftp.hooks.sftp.SFTPHook",
+                default_conn_name=None,
+                hook_name="Simple Mail Transfer Protocol (SMTP)",
+                standard_fields=StandardHookFields(
+                    description=ConnectionHookFieldBehavior(),
+                    url_schema=ConnectionHookFieldBehavior(),
+                    host=ConnectionHookFieldBehavior(),
+                    port=ConnectionHookFieldBehavior(),
+                    login=ConnectionHookFieldBehavior(),
+                    password=ConnectionHookFieldBehavior(),
+                ),
+                extra_fields=extra_fields,
+            )
+        ]
+        response = test_client.get("/connections/hook_meta")
+        assert response.status_code == 200
+        body = response.json()
+        assert body[0]["extra_fields"] == expected_response
+
     def test_should_respond_401(self, unauthenticated_test_client):
         response = unauthenticated_test_client.get("/connections/hook_meta")
         assert response.status_code == 401

Reply via email to