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 ff17dc6790d Keycloak auth manager. Handle invalid resource (#51461)
ff17dc6790d is described below
commit ff17dc6790d113c053f4a57845a9ea95b06f056a
Author: Vincent <[email protected]>
AuthorDate: Fri Jun 6 10:16:20 2025 -0400
Keycloak auth manager. Handle invalid resource (#51461)
---
.../keycloak/auth_manager/keycloak_auth_manager.py | 12 +++++
.../auth_manager/test_keycloak_auth_manager.py | 53 +++++++++++++++++++++-
2 files changed, 64 insertions(+), 1 deletion(-)
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 9857e8d1fd2..d4416ab78ef 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
@@ -16,6 +16,8 @@
# under the License.
from __future__ import annotations
+import json
+import logging
from typing import TYPE_CHECKING, Any
from urllib.parse import urljoin
@@ -45,6 +47,8 @@ if TYPE_CHECKING:
)
from airflow.api_fastapi.common.types import MenuItem
+log = logging.getLogger(__name__)
+
class KeycloakAuthManager(BaseAuthManager[KeycloakAuthManagerUser]):
"""
@@ -246,6 +250,14 @@ class
KeycloakAuthManager(BaseAuthManager[KeycloakAuthManagerUser]):
return True
if resp.status_code == 403:
return False
+ if resp.status_code == 400:
+ error = json.loads(resp.text)
+ if error.get("error") == "invalid_resource":
+ log.debug(error["error_description"])
+ return False
+ raise AirflowException(
+ f"Request not recognized by Keycloak. {error.get('error')}.
{error.get('error_description')}"
+ )
raise AirflowException(f"Unexpected error: {resp.status_code} -
{resp.text}")
@staticmethod
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 f9de61de79a..e397044b86b 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
@@ -16,6 +16,7 @@
# under the License.
from __future__ import annotations
+import json
from unittest.mock import Mock, call, patch
import pytest
@@ -204,9 +205,59 @@ class TestKeycloakAuthManager:
resp.status_code = 500
mock_requests.post.return_value = resp
- with pytest.raises(AirflowException):
+ with pytest.raises(AirflowException) as e:
getattr(auth_manager, function)(method="GET", user=user)
+ assert "Unexpected error" in str(e.value)
+
+ @pytest.mark.parametrize(
+ "function",
+ [
+ "is_authorized_configuration",
+ "is_authorized_connection",
+ "is_authorized_backfill",
+ "is_authorized_asset",
+ "is_authorized_asset_alias",
+ "is_authorized_variable",
+ "is_authorized_pool",
+ ],
+ )
+
@patch("airflow.providers.keycloak.auth_manager.keycloak_auth_manager.requests")
+ def test_is_authorized_invalid_resource(self, mock_requests, function,
auth_manager, user):
+ resp = Mock()
+ resp.status_code = 400
+ resp.text = json.dumps(
+ {"error": "invalid_resource", "error_description": "Resource with
id [Pool] does not exist."}
+ )
+ mock_requests.post.return_value = resp
+
+ result = getattr(auth_manager, function)(method="GET", user=user)
+ assert result is False
+
+ @pytest.mark.parametrize(
+ "function",
+ [
+ "is_authorized_configuration",
+ "is_authorized_connection",
+ "is_authorized_backfill",
+ "is_authorized_asset",
+ "is_authorized_asset_alias",
+ "is_authorized_variable",
+ "is_authorized_pool",
+ ],
+ )
+
@patch("airflow.providers.keycloak.auth_manager.keycloak_auth_manager.requests")
+ def test_is_authorized_invalid_request(self, mock_requests, function,
auth_manager, user):
+ resp = Mock()
+ resp.status_code = 400
+ resp.text = json.dumps({"error": "invalid_scope", "error_description":
"Invalid scopes: GET"})
+ mock_requests.post.return_value = resp
+
+ with pytest.raises(AirflowException) as e:
+ getattr(auth_manager, function)(method="GET", user=user)
+
+ assert "Request not recognized by Keycloak. invalid_scope. Invalid
scopes: GET" in str(e.value)
+
@pytest.mark.parametrize(
"status_code_type, status_code_resource, expected_one_call,
expected_two_calls",
[