From d3dd685ad4b2cac710d419abfca89909723bff76 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sat, 21 Feb 2026 12:11:19 +0100 Subject: [PATCH] [PR #11515/7cd75945 backport][stable-12] #11502 Fix mapping of config of keycloak_user_federation (#11529) #11502 Fix mapping of config of keycloak_user_federation (#11515) * #11502 Fix mapping of config Fix mapping of config Fix diff for mappers * Fix formatting with nox * Update changelogs/fragments/11502-keycloak-config-mapper.yaml * Remove duplicate comment https://github.com/ansible-collections/community.general/pull/11515#discussion_r2821444756 --------- (cherry picked from commit 7cd75945b289f206055241ffceb668e7243282d2) Co-authored-by: mixman68 Co-authored-by: Felix Fontein --- .../11502-keycloak-config-mapper.yaml | 2 ++ plugins/modules/keycloak_user_federation.py | 33 +++++++++++++++---- 2 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 changelogs/fragments/11502-keycloak-config-mapper.yaml diff --git a/changelogs/fragments/11502-keycloak-config-mapper.yaml b/changelogs/fragments/11502-keycloak-config-mapper.yaml new file mode 100644 index 0000000000..b7c1c1d186 --- /dev/null +++ b/changelogs/fragments/11502-keycloak-config-mapper.yaml @@ -0,0 +1,2 @@ +bugfixes: + - "keycloak_user_federation - mapper config item can be an array (https://github.com/ansible-collections/community.general/issues/11502, https://github.com/ansible-collections/community.general/pull/11515)." diff --git a/plugins/modules/keycloak_user_federation.py b/plugins/modules/keycloak_user_federation.py index c448d0247f..479d4ec015 100644 --- a/plugins/modules/keycloak_user_federation.py +++ b/plugins/modules/keycloak_user_federation.py @@ -744,15 +744,30 @@ def normalize_kc_comp(comp): def sanitize(comp): + def sanitize_value(v): + """Convert list values: single-element lists to strings, multi-element lists sorted alphabetically, others as-is.""" + if isinstance(v, list): + if len(v) == 0: + return None + elif len(v) == 1: + return v[0] + else: + return sorted(v) + else: + return v + compcopy = deepcopy(comp) if "config" in compcopy: - compcopy["config"] = {k: v[0] for k, v in compcopy["config"].items()} + compcopy["config"] = {k: sanitize_value(v) for k, v in compcopy["config"].items()} + # Remove None values (empty lists converted) + compcopy["config"] = {k: v for k, v in compcopy["config"].items() if v is not None} if "bindCredential" in compcopy["config"]: compcopy["config"]["bindCredential"] = "**********" if "mappers" in compcopy: for mapper in compcopy["mappers"]: if "config" in mapper: - mapper["config"] = {k: v[0] for k, v in mapper["config"].items()} + mapper["config"] = {k: sanitize_value(v) for k, v in mapper["config"].items()} + mapper["config"] = {k: v for k, v in mapper["config"].items() if v is not None} return compcopy @@ -886,11 +901,15 @@ def main(): if mappers is not None: for mapper in mappers: if mapper.get("config") is not None: - mapper["config"] = { - k: [str(v).lower() if not isinstance(v, str) else v] - for k, v in mapper["config"].items() - if mapper["config"][k] is not None - } + new_config = {} + for k, v in mapper["config"].items(): + if v is None: + continue + if isinstance(v, list): + new_config[k] = [str(item).lower() if not isinstance(item, str) else item for item in v] + else: + new_config[k] = [str(v).lower() if not isinstance(v, str) else v] + mapper["config"] = new_config # Filter and map the parameters names that apply comp_params = [