This is an automated email from the ASF dual-hosted git repository.
vincbeck 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 b4114239a89 Handle missing Keycloak resources as access denied (#68951)
b4114239a89 is described below
commit b4114239a895883f398c8d61e6c016c85c3bbf47
Author: Arnav <[email protected]>
AuthorDate: Fri Jun 26 00:47:21 2026 +0530
Handle missing Keycloak resources as access denied (#68951)
---
.../keycloak/auth_manager/keycloak_auth_manager.py | 10 ++++++
.../auth_manager/test_keycloak_auth_manager.py | 37 ++++++++++++++++++++++
2 files changed, 47 insertions(+)
diff --git
a/providers/keycloak/src/airflow/providers/keycloak/auth_manager/keycloak_auth_manager.py
b/providers/keycloak/src/airflow/providers/keycloak/auth_manager/keycloak_auth_manager.py
index a8cd683ef46..545f7803994 100644
---
a/providers/keycloak/src/airflow/providers/keycloak/auth_manager/keycloak_auth_manager.py
+++
b/providers/keycloak/src/airflow/providers/keycloak/auth_manager/keycloak_auth_manager.py
@@ -78,6 +78,13 @@ if TYPE_CHECKING:
log = logging.getLogger(__name__)
RESOURCE_ID_ATTRIBUTE_NAME = "resource_id"
+KEYCLOAK_RESOURCE_NOT_FOUND_ERROR = "resource not found:"
+
+
+def _is_missing_keycloak_resource_response(status_code: int, text: Any) ->
bool:
+ return status_code == 500 and isinstance(text, str) and
KEYCLOAK_RESOURCE_NOT_FOUND_ERROR in text.lower()
+
+
TEAM_SCOPED_RESOURCES = frozenset(
{
KeycloakResource.CONNECTION,
@@ -434,6 +441,9 @@ class
KeycloakAuthManager(BaseAuthManager[KeycloakAuthManagerUser]):
raise AirflowException(
f"Request not recognized by Keycloak. {error.get('error')}.
{error.get('error_description')}"
)
+ if _is_missing_keycloak_resource_response(resp.status_code, resp.text):
+ log.warning("Keycloak authorization resource is missing; denying
access. Response: %s", resp.text)
+ return False
raise AirflowException(f"Unexpected error: {resp.status_code} -
{resp.text}")
def filter_authorized_dag_ids(
diff --git
a/providers/keycloak/tests/unit/keycloak/auth_manager/test_keycloak_auth_manager.py
b/providers/keycloak/tests/unit/keycloak/auth_manager/test_keycloak_auth_manager.py
index 9c8ed9dd5b6..347e9944e5e 100644
---
a/providers/keycloak/tests/unit/keycloak/auth_manager/test_keycloak_auth_manager.py
+++
b/providers/keycloak/tests/unit/keycloak/auth_manager/test_keycloak_auth_manager.py
@@ -386,6 +386,19 @@ class TestKeycloakAuthManager:
assert "Unexpected error" in str(e.value)
+ def test_is_authorized_missing_keycloak_resource(self, auth_manager, user,
caplog):
+ resp = Mock()
+ resp.status_code = 500
+ resp.text = "resource not found: Dag:team-a"
+ auth_manager.http_session.post = Mock(return_value=resp)
+ caplog.set_level("WARNING",
logger="airflow.providers.keycloak.auth_manager.keycloak_auth_manager")
+
+ result = auth_manager.is_authorized_dag(method="GET",
details=DagDetails(id="dag_0"), user=user)
+
+ assert result is False
+ assert "Keycloak authorization resource is missing; denying access" in
caplog.text
+ assert "resource not found: Dag:team-a" in caplog.text
+
@pytest.mark.parametrize(
"function",
[
@@ -652,6 +665,30 @@ class TestKeycloakAuthManager:
mock_is_authorized.assert_called_once()
assert result == {"dag-a"}
+ @pytest.mark.skipif(not AIRFLOW_V_3_2_PLUS, reason="team_name not
supported before Airflow 3.2.0")
+ def test_filter_authorized_dag_ids_missing_keycloak_resource(self,
auth_manager_multi_team, user, caplog):
+ def post_response(*_, data, **__):
+ claims = json.loads(base64.b64decode(data["claim_token"]).decode())
+ dag_id = claims[RESOURCE_ID_ATTRIBUTE_NAME][0]
+ response = Mock()
+ if dag_id == "dag-missing":
+ response.status_code = 500
+ response.text = "resource not found: Dag:team-a"
+ else:
+ response.status_code = 200
+ return response
+
+ auth_manager_multi_team.http_session.post =
Mock(side_effect=post_response)
+ caplog.set_level("WARNING",
logger="airflow.providers.keycloak.auth_manager.keycloak_auth_manager")
+
+ result = auth_manager_multi_team.filter_authorized_dag_ids(
+ dag_ids={"dag-allowed", "dag-missing"}, user=user,
team_name="team-a"
+ )
+
+ assert result == {"dag-allowed"}
+ assert auth_manager_multi_team.http_session.post.call_count == 2
+ assert "Keycloak authorization resource is missing; denying access" in
caplog.text
+
@pytest.mark.skipif(not AIRFLOW_V_3_2_PLUS, reason="team_name not
supported before Airflow 3.2.0")
@patch.object(KeycloakAuthManager, "is_authorized_pool",
return_value=False)
def test_filter_authorized_pools_no_team_returns_empty(