diff --git a/airflow/api_connexion/endpoints/config_endpoint.py b/airflow/api_connexion/endpoints/config_endpoint.py index 38f6f32c22b2d..a6fc67beb7bf5 100644 --- a/airflow/api_connexion/endpoints/config_endpoint.py +++ b/airflow/api_connexion/endpoints/config_endpoint.py @@ -123,7 +123,7 @@ def get_value(section: str, option: str) -> Response: "Config not found.", detail=f"The option [{section}/{option}] is not found in config." ) - if (section, option) in conf.sensitive_config_values: + if (section.lower(), option.lower()) in conf.sensitive_config_values: value = "< hidden >" else: value = conf.get(section, option) diff --git a/airflow/configuration.py b/airflow/configuration.py index 0107cb1021a42..e43c1d81a5874 100644 --- a/airflow/configuration.py +++ b/airflow/configuration.py @@ -314,7 +314,11 @@ def sensitive_config_values(self) -> Set[tuple[str, str]]: # noqa: UP006 for s, s_c in self.configuration_description.items() for k, item in s_c.get("options").items() # type: ignore[union-attr] } - sensitive = {(section, key) for (section, key), v in flattened.items() if v.get("sensitive") is True} + sensitive = { + (section.lower(), key.lower()) + for (section, key), v in flattened.items() + if v.get("sensitive") is True + } depr_option = {self.deprecated_options[x][:-1] for x in sensitive if x in self.deprecated_options} depr_section = { (self.deprecated_sections[s][0], k) for s, k in sensitive if s in self.deprecated_sections diff --git a/tests/api_connexion/endpoints/test_config_endpoint.py b/tests/api_connexion/endpoints/test_config_endpoint.py index c8d309b0bdf62..17a159a4fb8a4 100644 --- a/tests/api_connexion/endpoints/test_config_endpoint.py +++ b/tests/api_connexion/endpoints/test_config_endpoint.py @@ -247,17 +247,26 @@ def test_should_respond_200_text_plain(self, mock_as_dict): return_value=MOCK_CONF_WITH_SENSITIVE_VALUE, ) @conf_vars({("webserver", "expose_config"): "non-sensitive-only"}) - def test_should_respond_200_text_plain_with_non_sensitive_only(self, mock_as_dict): + @pytest.mark.parametrize( + "section, option", + [ + ("core", "sql_alchemy_conn"), + ("core", "SQL_ALCHEMY_CONN"), + ("corE", "sql_alchemy_conn"), + ("CORE", "sql_alchemy_conn"), + ], + ) + def test_should_respond_200_text_plain_with_non_sensitive_only(self, mock_as_dict, section, option): response = self.client.get( - "/api/v1/config/section/core/option/sql_alchemy_conn", + f"/api/v1/config/section/{section}/option/{option}", headers={"Accept": "text/plain"}, environ_overrides={"REMOTE_USER": "test"}, ) assert response.status_code == 200 expected = textwrap.dedent( - """\ - [core] - sql_alchemy_conn = < hidden > + f"""\ + [{section}] + {option} = < hidden > """ ) assert expected == response.data.decode()