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 3c37b4794f8 Fix Keycloak access for individual roles (#70906)
3c37b4794f8 is described below
commit 3c37b4794f8ffb3dce83622561932c80551bf9ff
Author: D.H.KIM <[email protected]>
AuthorDate: Sat Aug 8 11:19:31 2026 +0900
Fix Keycloak access for individual roles (#70906)
* Fix Keycloak team Dag access for non-admin roles
Team ReadOnly permissions combined mutually exclusive role policies with an
UNANIMOUS strategy, preventing non-admin team members from reading team Dags.
* Fix Keycloak global admin access for individual roles
Admin and SuperAdmin are alternative global administrator roles, but the
current policy combination requires users to hold both roles.\n\nExisting
Keycloak objects are external state, so operators need clear upgrade guidance
to apply the corrected policy.
* Preserve global Admin access when creating teams
Deployments that enable multi-team mode later must keep global access
independent of whether both Admin and SuperAdmin policies are attached.
---
providers/keycloak/docs/changelog.rst | 10 +++++
.../keycloak/auth_manager/cli/commands.py | 4 ++
.../keycloak/auth_manager/cli/test_commands.py | 50 ++++++++++------------
3 files changed, 37 insertions(+), 27 deletions(-)
diff --git a/providers/keycloak/docs/changelog.rst
b/providers/keycloak/docs/changelog.rst
index 35f18bbee0b..7f1ff247328 100644
--- a/providers/keycloak/docs/changelog.rst
+++ b/providers/keycloak/docs/changelog.rst
@@ -25,6 +25,16 @@
Changelog
---------
+.. note::
+ Upgrading the provider does not update existing Keycloak permissions. For
each existing team,
+ run ``airflow keycloak-auth-manager create-team <team>`` again to update
the team ReadOnly
+ permission. For non-team installations, run
+ ``airflow keycloak-auth-manager create-permissions`` again without
``--teams`` to update the
+ global Admin permission.
+
+ Manually added policies attached to these permissions will also be
evaluated under the
+ ``AFFIRMATIVE`` strategy after the update.
+
0.9.0
.....
diff --git
a/providers/keycloak/src/airflow/providers/keycloak/auth_manager/cli/commands.py
b/providers/keycloak/src/airflow/providers/keycloak/auth_manager/cli/commands.py
index 6ead332155b..adb9aac6ff8 100644
---
a/providers/keycloak/src/airflow/providers/keycloak/auth_manager/cli/commands.py
+++
b/providers/keycloak/src/airflow/providers/keycloak/auth_manager/cli/commands.py
@@ -301,6 +301,7 @@ def _attach_default_role_permissions(
policy_name=_role_policy_name(role_name),
scope_names=_get_extended_resource_methods() + ["LIST"],
resource_names=[],
+ decision_strategy="AFFIRMATIVE",
_dry_run=_dry_run,
)
@@ -792,6 +793,7 @@ def _attach_team_permissions(
policy_name=_team_role_policy_name(team, "Viewer"),
scope_names=["GET", "LIST"],
resource_names=team_readable_resources,
+ decision_strategy="AFFIRMATIVE",
_dry_run=_dry_run,
)
for role_name in ("User", "Op", "Admin"):
@@ -802,6 +804,7 @@ def _attach_team_permissions(
policy_name=_team_role_policy_name(team, role_name),
scope_names=["GET", "LIST"],
resource_names=team_readable_resources,
+ decision_strategy="AFFIRMATIVE",
_dry_run=_dry_run,
)
_attach_policy_to_scope_permission(
@@ -926,6 +929,7 @@ def _attach_superadmin_permissions(
policy_name=_role_policy_name(SUPER_ADMIN_ROLE_NAME),
scope_names=_get_extended_resource_methods() + ["LIST"],
resource_names=team_scoped_resources,
+ decision_strategy="AFFIRMATIVE",
_dry_run=_dry_run,
)
_attach_policy_to_scope_permission(
diff --git
a/providers/keycloak/tests/unit/keycloak/auth_manager/cli/test_commands.py
b/providers/keycloak/tests/unit/keycloak/auth_manager/cli/test_commands.py
index 62c61f95f72..450996ad0a4 100644
--- a/providers/keycloak/tests/unit/keycloak/auth_manager/cli/test_commands.py
+++ b/providers/keycloak/tests/unit/keycloak/auth_manager/cli/test_commands.py
@@ -472,24 +472,17 @@ class TestCommands:
decision_strategy="AFFIRMATIVE",
_dry_run=False,
)
- mock_attach_scope_policy.assert_any_call(
- client,
- "test-id",
- permission_name="Admin",
- policy_name="Allow-Admin",
- scope_names=_get_extended_resource_methods() + ["LIST"],
- resource_names=[],
- _dry_run=False,
- )
- mock_attach_scope_policy.assert_any_call(
- client,
- "test-id",
- permission_name="Admin",
- policy_name="Allow-SuperAdmin",
- scope_names=_get_extended_resource_methods() + ["LIST"],
- resource_names=[],
- _dry_run=False,
- )
+ for role_name in ("Admin", SUPER_ADMIN_ROLE_NAME):
+ mock_attach_scope_policy.assert_any_call(
+ client,
+ "test-id",
+ permission_name="Admin",
+ policy_name=f"Allow-{role_name}",
+ scope_names=_get_extended_resource_methods() + ["LIST"],
+ resource_names=[],
+ decision_strategy="AFFIRMATIVE",
+ _dry_run=False,
+ )
mock_attach_resource_policy.assert_any_call(
client,
"test-id",
@@ -567,15 +560,17 @@ class TestCommands:
mock_update_admin_permission_resources.assert_called_once_with(client,
"test-id", _dry_run=False)
mock_ensure_group_policy.assert_called_once_with(client, "test-id",
"team-a", _dry_run=False)
assert mock_ensure_aggregate_policy.call_count == 4
- mock_attach_policy.assert_any_call(
- client,
- "test-id",
- permission_name="ReadOnly-team-a",
- policy_name="Allow-Viewer-team-a",
- scope_names=["GET", "LIST"],
- resource_names=["Dag:team-a", "Team:team-a"],
- _dry_run=False,
- )
+ for role_name in TEAM_ROLE_NAMES:
+ mock_attach_policy.assert_any_call(
+ client,
+ "test-id",
+ permission_name="ReadOnly-team-a",
+ policy_name=f"Allow-{role_name}-team-a",
+ scope_names=["GET", "LIST"],
+ resource_names=["Dag:team-a", "Team:team-a"],
+ decision_strategy="AFFIRMATIVE",
+ _dry_run=False,
+ )
mock_attach_policy.assert_any_call(
client,
"test-id",
@@ -644,6 +639,7 @@ class TestCommands:
"Team:team-a",
"Variable:team-a",
],
+ decision_strategy="AFFIRMATIVE",
_dry_run=False,
)
mock_attach_policy.assert_any_call(