1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-03-22 05:09:12 +00:00

[PR #11455/af4dbafe backport][stable-12] keycloak_client: fix diff for keycloak client auth flow overrides (#11477)

keycloak_client: fix diff for keycloak client auth flow overrides (#11455)

* 11430: fix diff for keycloak client auth flow overrides

* 11430: add changelog fragment

* 11430: move util function merge_settings_without_absent_nulls to the util functions file _keycloak_utils

* 11443: code cleanup

---------


(cherry picked from commit af4dbafe86)

Co-authored-by: thomasbargetz <thomas.bargetz@gmail.com>
Co-authored-by: Thomas Bargetz <thomas.bargetz@rise-world.com>
This commit is contained in:
patchback[bot] 2026-02-07 16:34:29 +01:00 committed by GitHub
parent 88bfb6dda3
commit a0d6487f6d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 47 additions and 8 deletions

View file

@ -0,0 +1,2 @@
bugfixes:
- keycloak_client - fix idempotency bug caused by ``null`` flow overrides value differences for non-existing flow overrides (https://github.com/ansible-collections/community.general/issues/11430, https://github.com/ansible-collections/community.general/pull/11455).

View file

@ -0,0 +1,32 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# Note that this module util is **PRIVATE** to the collection. It can have breaking changes at any time.
# Do not use this from other collections or standalone plugins/modules!
from __future__ import annotations
import typing as t
def merge_settings_without_absent_nulls(
existing_settings: dict[str, t.Any], desired_settings: dict[str, t.Any]
) -> dict[str, t.Any]:
"""
Merges existing and desired settings into a new dictionary while excluding null values in desired settings that are absent in the existing settings.
This ensures idempotency by treating absent keys in existing settings and null values in desired settings as equivalent, preventing unnecessary updates.
Args:
existing_settings (dict): Dictionary representing the current settings in Keycloak
desired_settings (dict): Dictionary representing the desired settings
Returns:
dict: A new dictionary containing all entries from existing_settings and desired_settings,
excluding null values in desired_settings whose corresponding keys are not present in existing_settings
"""
existing = existing_settings or {}
desired = desired_settings or {}
return {**existing, **{k: v for k, v in desired.items() if v is not None or k in existing}}

View file

@ -748,6 +748,9 @@ import copy
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.identity.keycloak._keycloak_utils import (
merge_settings_without_absent_nulls,
)
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import ( from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import (
KeycloakAPI, KeycloakAPI,
KeycloakError, KeycloakError,
@ -1316,15 +1319,17 @@ def main():
if client_param == "protocol_mappers": if client_param == "protocol_mappers":
new_param_value = [{k: v for k, v in x.items() if v is not None} for x in new_param_value] new_param_value = [{k: v for k, v in x.items() if v is not None} for x in new_param_value]
elif client_param == "authentication_flow_binding_overrides": elif client_param == "authentication_flow_binding_overrides":
new_param_value = flow_binding_from_dict_to_model(new_param_value, realm, kc) desired_flow_binding_overrides = flow_binding_from_dict_to_model(new_param_value, realm, kc)
elif client_param == "attributes" and "attributes" in before_client: existing_flow_binding_overrides = before_client.get("authenticationFlowBindingOverrides")
attributes_copy = copy.deepcopy(before_client["attributes"]) # ensures idempotency
# Merge client attributes while excluding null-valued attributes that are not present in Keycloak's response. new_param_value = merge_settings_without_absent_nulls(
# This ensures idempotency by treating absent attributes and null attributes as equivalent. existing_flow_binding_overrides, desired_flow_binding_overrides
attributes_copy.update(
{key: value for key, value in new_param_value.items() if value is not None or key in attributes_copy}
) )
new_param_value = attributes_copy elif client_param == "attributes" and "attributes" in before_client:
desired_attributes = new_param_value
existing_attributes = copy.deepcopy(before_client["attributes"])
# ensures idempotency
new_param_value = merge_settings_without_absent_nulls(existing_attributes, desired_attributes)
elif client_param in ["clientScopesBehavior", "client_scopes_behavior"]: elif client_param in ["clientScopesBehavior", "client_scopes_behavior"]:
continue continue