From 22f31d02f28a7eb384a589bfa99dbf3a2279ead0 Mon Sep 17 00:00:00 2001 From: Rafael Guterres Jeffman <rjeffman@redhat.com> Date: Wed, 20 Oct 2021 19:47:43 -0300 Subject: [PATCH] sudorule: Fix runas with external users and groups. When setting 'runasuser' or 'runasgroup' for a sudorule, either IPA or external users and groups can be used, but only IPA users and groups were being searched for when modifying the attributes, making this task not idempotent if an external group or user was used.. This patch fixes this issue by comparing users and groups to the IPA and external setting. The IPA CLI commands are slightly confusing, as the sudorule-add and sudorule-mod display separate options for internal and external users and groups, but these options are deprecated and do not work anymore, in favor of sudorule-add-runasuser and sudorule-add-runasgroup, which don't diferentiate between internal and external users, from the CLI user perspective. --- plugins/modules/ipasudorule.py | 96 +++++++++++++++++++---- tests/sudorule/test_sudorule.yml | 130 +++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+), 13 deletions(-) diff --git a/plugins/modules/ipasudorule.py b/plugins/modules/ipasudorule.py index 8d72d893..d5a459a8 100644 --- a/plugins/modules/ipasudorule.py +++ b/plugins/modules/ipasudorule.py @@ -456,11 +456,31 @@ def main(): sudooption_add, sudooption_del = gen_add_del_lists( sudooption, res_find.get('ipasudoopt', [])) + # runasuser attribute can be used with both IPA and + # non-IPA (external) users. IPA will handle the correct + # attribute to properly store data, so we need to compare + # the provided list against both users and external + # users list. runasuser_add, runasuser_del = gen_add_del_lists( - runasuser, res_find.get('ipasudorunas_user', [])) - + runasuser, + ( + res_find.get('ipasudorunas_user', []) + + res_find.get('ipasudorunasextuser', []) + ) + ) + + # runasgroup attribute can be used with both IPA and + # non-IPA (external) groups. IPA will handle the correct + # attribute to properly store data, so we need to compare + # the provided list against both groups and external + # groups list. runasgroup_add, runasgroup_del = gen_add_del_lists( - runasgroup, res_find.get('ipasudorunas_group', [])) + runasgroup, + ( + res_find.get('ipasudorunas_group', []) + + res_find.get('ipasudorunasextgroup', []) + ) + ) # Add hosts and hostgroups if len(host_add) > 0 or len(hostgroup_add) > 0: @@ -593,14 +613,38 @@ def main(): "ipasudoopt" in res_find: sudooption = gen_add_list( sudooption, res_find["ipasudoopt"]) - if runasuser is not None and \ - "ipasudorunas_user" in res_find: + # runasuser attribute can be used with both IPA and + # non-IPA (external) users, so we need to compare + # the provided list against both users and external + # users list. + if ( + runasuser is not None + and ( + "ipasudorunas_user" in res_find + or "ipasudorunasextuser" in res_find + ) + ): runasuser = gen_add_list( - runasuser, res_find["ipasudorunas_user"]) - if runasgroup is not None and \ - "ipasudorunasgroup_group" in res_find: + runasuser, + (list(res_find.get('ipasudorunas_user', [])) + + list(res_find.get('ipasudorunasextuser', []))) + ) + # runasgroup attribute can be used with both IPA and + # non-IPA (external) groups, so we need to compare + # the provided list against both users and external + # groups list. + if ( + runasgroup is not None + and ( + "ipasudorunasgroup_group" in res_find + or "ipasudorunasextgroup" in res_find + ) + ): runasgroup = gen_add_list( - runasgroup, res_find["ipasudorunasgroup_group"]) + runasgroup, + (list(res_find.get("ipasudorunasgroup_group", [])) + + list(res_find.get("ipasudorunasextgroup", []))) + ) # Add hosts and hostgroups if host is not None or hostgroup is not None: @@ -724,17 +768,43 @@ def main(): sudooption, res_find["ipasudoopt"]) else: sudooption = None + # runasuser attribute can be used with both IPA and + # non-IPA (external) users, so we need to compare + # the provided list against both users and external + # users list. if runasuser is not None: - if "ipasudorunas_user" in res_find: + if ( + "ipasudorunas_user" in res_find + or "ipasudorunasextuser" in res_find + ): runasuser = gen_intersection_list( - runasuser, res_find["ipasudorunas_user"]) + runasuser, + ( + list(res_find.get('ipasudorunas_user', [])) + + list(res_find.get( + 'ipasudorunasextuser', [])) + ) + ) else: runasuser = None + # runasgroup attribute can be used with both IPA and + # non-IPA (external) groups, so we need to compare + # the provided list against both groups and external + # groups list. if runasgroup is not None: - if "ipasudorunasgroup_group" in res_find: + if ( + "ipasudorunasgroup_group" in res_find + or "ipasudorunasextgroup" in res_find + ): runasgroup = gen_intersection_list( runasgroup, - res_find["ipasudorunasgroup_group"]) + ( + list(res_find.get( + "ipasudorunasgroup_group", [])) + + list(res_find.get( + "ipasudorunasextgroup", [])) + ) + ) else: runasgroup = None diff --git a/tests/sudorule/test_sudorule.yml b/tests/sudorule/test_sudorule.yml index 92ea773f..918ab5bf 100644 --- a/tests/sudorule/test_sudorule.yml +++ b/tests/sudorule/test_sudorule.yml @@ -73,6 +73,7 @@ ipaadmin_password: SomeADMINpassword ipaapi_context: "{{ ipa_context | default(omit) }}" name: + - test_upstream_issue_664 - testrule1 - allusers - allhosts @@ -755,6 +756,134 @@ register: result failed_when: result.changed or result.failed + - name: Ensure sudorule with external user in 'runasuser' is present + ipasudorule: + ipaadmin_password: SomeADMINpassword + name: test_upstream_issue_664 + runasuser: + - apache + register: result + failed_when: not result.changed or result.failed + + - name: Ensure sudorule with external user in 'runasuser' is present, again + ipasudorule: + ipaadmin_password: SomeADMINpassword + name: test_upstream_issue_664 + runasuser: + - apache + register: result + failed_when: result.changed or result.failed + + - name: Ensure sudorule member external user in 'runasuser' is absent + ipasudorule: + ipaadmin_password: SomeADMINpassword + name: test_upstream_issue_664 + runasuser: + - apache + action: member + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Ensure sudorule member external user in 'runasuser' is absent, again + ipasudorule: + ipaadmin_password: SomeADMINpassword + name: test_upstream_issue_664 + runasuser: + - apache + action: member + state: absent + register: result + failed_when: result.changed or result.failed + + - name: Ensure sudorule member external user in 'runasuser' is present + ipasudorule: + ipaadmin_password: SomeADMINpassword + name: test_upstream_issue_664 + runasuser: + - apache + action: member + register: result + failed_when: not result.changed or result.failed + + - name: Ensure sudorule member external user in 'runasuser' is present, again + ipasudorule: + ipaadmin_password: SomeADMINpassword + name: test_upstream_issue_664 + runasuser: + - apache + action: member + register: result + failed_when: result.changed or result.failed + + - name: Ensure sudorule with external group in 'runasgroup' is present + ipasudorule: + ipaadmin_password: SomeADMINpassword + name: test_upstream_issue_664 + runasgroup: + - wheel + register: result + failed_when: not result.changed or result.failed + + - name: Ensure sudorule with external group in 'runasgroup' user is present, again + ipasudorule: + ipaadmin_password: SomeADMINpassword + name: test_upstream_issue_664 + runasgroup: + - wheel + register: result + failed_when: result.changed or result.failed + + - name: Ensure sudorule member external group in 'runasgroup' is absent + ipasudorule: + ipaadmin_password: SomeADMINpassword + name: test_upstream_issue_664 + runasgroup: + - wheel + action: member + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Ensure sudorule member external group in 'runasgroup' is absent, again + ipasudorule: + ipaadmin_password: SomeADMINpassword + name: test_upstream_issue_664 + runasgroup: + - wheel + action: member + state: absent + register: result + failed_when: result.changed or result.failed + + - name: Ensure sudorule member external group in 'runasgroup' is present + ipasudorule: + ipaadmin_password: SomeADMINpassword + name: test_upstream_issue_664 + runasgroup: + - wheel + action: member + register: result + failed_when: not result.changed or result.failed + + - name: Ensure sudorule member external group in 'runasgroup' is present, again + ipasudorule: + ipaadmin_password: SomeADMINpassword + name: test_upstream_issue_664 + runasgroup: + - wheel + action: member + register: result + failed_when: result.changed or result.failed + + - name: Ensure sudorule 'test_upstream_issue_664' is absent + ipasudorule: + ipaadmin_password: SomeADMINpassword + name: test_upstream_issue_664 + state: absent + register: result + failed_when: not result.changed or result.failed + # cleanup - name: Ensure sudocmdgroup is absent ipasudocmdgroup: @@ -777,6 +906,7 @@ ipaadmin_password: SomeADMINpassword ipaapi_context: "{{ ipa_context | default(omit) }}" name: + - test_upstream_issue_664 - testrule1 - allusers - allhosts -- GitLab