From e1f4be1e014723960c44f05d3510a801b39e3fab Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 23:30:30 +0200 Subject: [PATCH] =?UTF-8?q?[PR=20#6688/032996e0=20backport][stable-6]=20Fi?= =?UTF-8?q?x=20composites=20comparison=20for=20role=20in=20is=5Fstruct=5Fi?= =?UTF-8?q?ncluded=20keycloak.py=20=E2=80=A6=20(#6689)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix composites comparison for role in is_struct_included keycloak.py … (#6688) * Fix composites comparison for role in is_struct_included keycloak.py function * Add changelog fragment and unit tests * Update changelogs/fragments/6688-is-struct-included-bug-in-keycloak-py.yml Co-authored-by: Felix Fontein --------- Co-authored-by: Felix Fontein (cherry picked from commit 032996e0052a514a17775c4492532c3505a7e142) Co-authored-by: Philippe Gauthier --- ...-is-struct-included-bug-in-keycloak-py.yml | 2 + .../identity/keycloak/keycloak.py | 12 +- .../keycloak/test_keycloak_module_utils.py | 103 ++++++++++++++++++ 3 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 changelogs/fragments/6688-is-struct-included-bug-in-keycloak-py.yml create mode 100644 tests/unit/plugins/module_utils/identity/keycloak/test_keycloak_module_utils.py diff --git a/changelogs/fragments/6688-is-struct-included-bug-in-keycloak-py.yml b/changelogs/fragments/6688-is-struct-included-bug-in-keycloak-py.yml new file mode 100644 index 0000000000..92546d8eca --- /dev/null +++ b/changelogs/fragments/6688-is-struct-included-bug-in-keycloak-py.yml @@ -0,0 +1,2 @@ +bugfixes: + - keycloak module utils - fix ``is_struct_included`` handling of lists of lists/dictionaries (https://github.com/ansible-collections/community.general/pull/6688). \ No newline at end of file diff --git a/plugins/module_utils/identity/keycloak/keycloak.py b/plugins/module_utils/identity/keycloak/keycloak.py index c35ca43c2c..7e421f3bb3 100644 --- a/plugins/module_utils/identity/keycloak/keycloak.py +++ b/plugins/module_utils/identity/keycloak/keycloak.py @@ -207,24 +207,30 @@ def is_struct_included(struct1, struct2, exclude=None): Return True if all element of dict 1 are present in dict 2, return false otherwise. """ if isinstance(struct1, list) and isinstance(struct2, list): + if not struct1 and not struct2: + return True for item1 in struct1: if isinstance(item1, (list, dict)): for item2 in struct2: - if not is_struct_included(item1, item2, exclude): - return False + if is_struct_included(item1, item2, exclude): + break + else: + return False else: if item1 not in struct2: return False return True elif isinstance(struct1, dict) and isinstance(struct2, dict): + if not struct1 and not struct2: + return True try: for key in struct1: if not (exclude and key in exclude): if not is_struct_included(struct1[key], struct2[key], exclude): return False - return True except KeyError: return False + return True elif isinstance(struct1, bool) and isinstance(struct2, bool): return struct1 == struct2 else: diff --git a/tests/unit/plugins/module_utils/identity/keycloak/test_keycloak_module_utils.py b/tests/unit/plugins/module_utils/identity/keycloak/test_keycloak_module_utils.py new file mode 100644 index 0000000000..dc0f8d3f9a --- /dev/null +++ b/tests/unit/plugins/module_utils/identity/keycloak/test_keycloak_module_utils.py @@ -0,0 +1,103 @@ +# 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 + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import unittest + +from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import is_struct_included + + +class KeycloakIsStructIncludedTestCase(unittest.TestCase): + dict1 = dict( + test1='test1', + test2=dict( + test1='test1', + test2='test2' + ), + test3=['test1', dict(test='test1', test2='test2')] + ) + dict2 = dict( + test1='test1', + test2=dict( + test1='test1', + test2='test2', + test3='test3' + ), + test3=['test1', dict(test='test1', test2='test2'), 'test3'], + test4='test4' + ) + dict3 = dict( + test1='test1', + test2=dict( + test1='test1', + test2='test23', + test3='test3' + ), + test3=['test1', dict(test='test1', test2='test23'), 'test3'], + test4='test4' + ) + + dict5 = dict( + test1='test1', + test2=dict( + test1=True, + test2='test23', + test3='test3' + ), + test3=['test1', dict(test='test1', test2='test23'), 'test3'], + test4='test4' + ) + + dict6 = dict( + test1='test1', + test2=dict( + test1='true', + test2='test23', + test3='test3' + ), + test3=['test1', dict(test='test1', test2='test23'), 'test3'], + test4='test4' + ) + dict7 = [ + { + 'roles': ['view-clients', 'view-identity-providers', 'view-users', 'query-realms', 'manage-users'], + 'clientid': 'master-realm' + }, + { + 'roles': ['manage-account', 'view-profile', 'manage-account-links'], + 'clientid': 'account' + } + ] + dict8 = [ + { + 'roles': ['view-clients', 'query-realms', 'view-users'], + 'clientid': 'master-realm' + }, + { + 'roles': ['manage-account-links', 'view-profile', 'manage-account'], + 'clientid': 'account' + } + ] + + def test_trivial(self): + self.assertTrue(is_struct_included(self.dict1, self.dict1)) + + def test_equals_with_dict2_bigger_than_dict1(self): + self.assertTrue(is_struct_included(self.dict1, self.dict2)) + + def test_not_equals_with_dict2_bigger_than_dict1(self): + self.assertFalse(is_struct_included(self.dict2, self.dict1)) + + def test_not_equals_with_dict1_different_than_dict3(self): + self.assertFalse(is_struct_included(self.dict1, self.dict3)) + + def test_equals_with_dict5_contain_bool_and_dict6_contain_true_string(self): + self.assertFalse(is_struct_included(self.dict5, self.dict6)) + self.assertFalse(is_struct_included(self.dict6, self.dict5)) + + def test_not_equals_dict7_dict8_compare_dict7_with_list_bigger_than_dict8_but_reverse_equals(self): + self.assertFalse(is_struct_included(self.dict7, self.dict8)) + self.assertTrue(is_struct_included(self.dict8, self.dict7))