Skip to content

Commit

Permalink
New utils method that returns the lists of members.
Browse files Browse the repository at this point in the history
Extend netgroup and sudorule modules to support external
users and hosts wherever possible.
Add tests for ipanetgroup and ipasudorule.

Signed-off-by: Denis Karpelevich <[email protected]>
  • Loading branch information
dkarpele committed Jan 13, 2023
1 parent 6cac891 commit ed98f82
Show file tree
Hide file tree
Showing 7 changed files with 513 additions and 33 deletions.
16 changes: 16 additions & 0 deletions plugins/module_utils/ansible_freeipa_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,22 @@ def gen_intersection_list(user_list, res_list):
return list(set(res_list or []).intersection(set(user_list or [])))


def get_attr_data(res, *args):
"""
Get the lists of members to pass on `gen_*` methods as a res_list argument.
This function should be used to get members (usually users,
external users, hosts, external hosts, etc.) with any action and
any state.
It is returning the concatenation of all attributes provided by user.
"""
res_list = []
for attribute in args:
res_list += res.get(attribute, [])
return list(set(res_list))


def encode_certificate(cert):
"""
Encode a certificate using base64.
Expand Down
14 changes: 10 additions & 4 deletions plugins/modules/ipanetgroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@

from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \
gen_add_list, gen_intersection_list, ensure_fqdn
gen_add_list, gen_intersection_list, get_attr_data, ensure_fqdn


def find_netgroup(module, name):
Expand Down Expand Up @@ -340,7 +340,9 @@ def main():
group, res_find.get("memberuser_group"))

host_add, host_del = gen_add_del_lists(
host, res_find.get("memberhost_host"))
host, get_attr_data(res_find,
"memberhost_host",
"externalhost"))

hostgroup_add, hostgroup_del = gen_add_del_lists(
hostgroup, res_find.get("memberhost_hostgroup"))
Expand All @@ -360,7 +362,9 @@ def main():
group_add = gen_add_list(
group, res_find.get("memberuser_group"))
host_add = gen_add_list(
host, res_find.get("memberhost_host"))
host, get_attr_data(res_find,
"memberhost_host",
"externalhost"))
hostgroup_add = gen_add_list(
hostgroup, res_find.get("memberhost_hostgroup"))
netgroup_add = gen_add_list(
Expand All @@ -379,7 +383,9 @@ def main():
group_del = gen_intersection_list(
group, res_find.get("memberuser_group"))
host_del = gen_intersection_list(
host, res_find.get("memberhost_host"))
host, get_attr_data(res_find,
"memberhost_host",
"externalhost"))
hostgroup_del = gen_intersection_list(
hostgroup, res_find.get("memberhost_hostgroup"))
netgroup_del = gen_intersection_list(
Expand Down
68 changes: 39 additions & 29 deletions plugins/modules/ipasudorule.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,8 @@

from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
gen_intersection_list, api_get_domain, ensure_fqdn, netaddr, to_text
gen_intersection_list, get_attr_data, api_get_domain, ensure_fqdn, \
netaddr, to_text


def find_sudorule(module, name):
Expand Down Expand Up @@ -507,7 +508,9 @@ def main():

# Generate addition and removal lists
host_add, host_del = gen_add_del_lists(
host, res_find.get('memberhost_host', []))
host, get_attr_data(res_find,
"memberhost_host",
"externalhost"))

hostgroup_add, hostgroup_del = gen_add_del_lists(
hostgroup, res_find.get('memberhost_hostgroup', []))
Expand All @@ -516,7 +519,9 @@ def main():
hostmask, res_find.get('hostmask', []))

user_add, user_del = gen_add_del_lists(
user, res_find.get('memberuser_user', []))
user, get_attr_data(res_find,
"memberuser_user",
"externaluser"))

group_add, group_del = gen_add_del_lists(
group, res_find.get('memberuser_group', []))
Expand Down Expand Up @@ -547,10 +552,9 @@ def main():
# users list.
runasuser_add, runasuser_del = gen_add_del_lists(
runasuser,
(
res_find.get('ipasudorunas_user', [])
+ res_find.get('ipasudorunasextuser', [])
)
get_attr_data(res_find,
'ipasudorunas_user',
'ipasudorunasextuser')
)

# runasgroup attribute can be used with both IPA and
Expand All @@ -560,10 +564,9 @@ def main():
# groups list.
runasgroup_add, runasgroup_del = gen_add_del_lists(
runasgroup,
(
res_find.get('ipasudorunasgroup_group', [])
+ res_find.get('ipasudorunasextgroup', [])
)
get_attr_data(res_find,
'ipasudorunasgroup_group',
'ipasudorunasextgroup')
)

elif action == "member":
Expand All @@ -577,7 +580,9 @@ def main():
# the sudorule already
if host is not None:
host_add = gen_add_list(
host, res_find.get("memberhost_host"))
host, get_attr_data(res_find,
"memberhost_host",
"externalhost"))
if hostgroup is not None:
hostgroup_add = gen_add_list(
hostgroup, res_find.get("memberhost_hostgroup"))
Expand All @@ -586,7 +591,9 @@ def main():
hostmask, res_find.get("hostmask"))
if user is not None:
user_add = gen_add_list(
user, res_find.get("memberuser_user"))
user, get_attr_data(res_find,
"memberuser_user",
"externaluser"))
if group is not None:
group_add = gen_add_list(
group, res_find.get("memberuser_group"))
Expand Down Expand Up @@ -620,8 +627,9 @@ def main():
if runasuser is not None:
runasuser_add = gen_add_list(
runasuser,
(list(res_find.get('ipasudorunas_user', []))
+ list(res_find.get('ipasudorunasextuser', [])))
get_attr_data(res_find,
'ipasudorunas_user',
'ipasudorunasextuser')
)
# runasgroup attribute can be used with both IPA and
# non-IPA (external) groups, so we need to compare
Expand All @@ -630,8 +638,9 @@ def main():
if runasgroup is not None:
runasgroup_add = gen_add_list(
runasgroup,
(list(res_find.get("ipasudorunasgroup_group", []))
+ list(res_find.get("ipasudorunasextgroup", [])))
get_attr_data(res_find,
'ipasudorunasgroup_group',
'ipasudorunasextgroup')
)

elif state == "absent":
Expand All @@ -650,7 +659,9 @@ def main():
# in sudorule
if host is not None:
host_del = gen_intersection_list(
host, res_find.get("memberhost_host"))
host, get_attr_data(res_find,
"memberhost_host",
"externalhost"))

if hostgroup is not None:
hostgroup_del = gen_intersection_list(
Expand All @@ -662,7 +673,9 @@ def main():

if user is not None:
user_del = gen_intersection_list(
user, res_find.get("memberuser_user"))
user, get_attr_data(res_find,
"memberuser_user",
"externaluser"))

if group is not None:
group_del = gen_intersection_list(
Expand Down Expand Up @@ -698,10 +711,10 @@ def main():
if runasuser is not None:
runasuser_del = gen_intersection_list(
runasuser,
(
list(res_find.get('ipasudorunas_user', []))
+ list(res_find.get('ipasudorunasextuser', []))
)
get_attr_data(res_find,
'ipasudorunas_user',
'ipasudorunasextuser')

)
# runasgroup attribute can be used with both IPA and
# non-IPA (external) groups, so we need to compare
Expand All @@ -710,12 +723,9 @@ def main():
if runasgroup is not None:
runasgroup_del = gen_intersection_list(
runasgroup,
(
list(res_find.get(
"ipasudorunasgroup_group", []))
+ list(res_find.get(
"ipasudorunasextgroup", []))
)
get_attr_data(res_find,
'ipasudorunasgroup_group',
'ipasudorunasextgroup')
)

elif state == "enabled":
Expand Down
132 changes: 132 additions & 0 deletions tests/netgroup/test_netgroup_ext_member.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---

- name: Test netgroup with external members
hosts: "{{ ipa_test_host | default('ipaserver') }}"
become: true
gather_facts: true
remote_user: root

tasks:
- name: Test netgroup with external members
block:
# setup
- name: Ensure netgroups are absent
ipanetgroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name:
- testnetgroup1
- testnetgroup2
state: absent

- name: Ensure external host is absent
ipahost:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name:
- external.host
state: absent

- name: Ensure host is present
ipahost:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: "{{ ansible_facts['fqdn'] }}"

- name: Ensure netgroup testnetgroup2 is present
ipanetgroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testnetgroup2

# tests
- name: Ensure netgroup is present with hosts (action netgroup)
ipanetgroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testnetgroup1
host:
- "{{ ansible_facts['fqdn'] }}"
- external.host
register: result
failed_when: not result.changed or result.failed

- name: Ensure netgroup is present with hosts (action netgroup) again
ipanetgroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testnetgroup1
host:
- "{{ ansible_facts['fqdn'] }}"
- external.host
register: result
failed_when: result.changed or result.failed

- name: Ensure netgroup is present with hosts (action member)
ipanetgroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testnetgroup2
host:
- "{{ ansible_facts['fqdn'] }}"
- external.host
action: member
register: result
failed_when: not result.changed or result.failed

- name: Ensure netgroup is present with hosts (action member) again
ipanetgroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testnetgroup2
host:
- "{{ ansible_facts['fqdn'] }}"
- external.host
action: member
register: result
failed_when: result.changed or result.failed

- name: Ensure hosts are absent in netgroup (action member)
ipanetgroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testnetgroup2
host:
- "{{ ansible_facts['fqdn'] }}"
- external.host
action: member
state: absent
register: result
failed_when: not result.changed or result.failed

- name: Ensure hosts are absent in netgroup (action member) again
ipanetgroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testnetgroup2
host:
- "{{ ansible_facts['fqdn'] }}"
- external.host
action: member
state: absent
register: result
failed_when: result.changed or result.failed

always:
# cleanup
- name: Ensure netgroups are absent
ipanetgroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name:
- testnetgroup1
- testnetgroup2
state: absent

- name: Ensure external host is absent
ipahost:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name:
- external.host
state: absent
41 changes: 41 additions & 0 deletions tests/netgroup/test_netgroup_ext_member_client_context.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---

- name: Test netgroup with external members, client context
hosts: ipaclients, ipaserver
become: no
gather_facts: no
remote_user: root

tasks:
- name: Include FreeIPA facts.
ansible.builtin.include_tasks: ../env_freeipa_facts.yml

# Test will only be executed if host is not a server.
- name: Execute with server context in the client.
ipanetgroup:
ipaadmin_password: SomeADMINpassword
ipaapi_context: server
name: ThisShouldNotWork
register: result
failed_when: not (result.failed and result.msg is regex("No module named '*ipaserver'*"))
when: ipa_host_is_client

# Import basic module tests, and execute with ipa_context set to 'client'.
# If ipaclients is set, it will be executed using the client, if not,
# ipaserver will be used.
#
# With this setup, tests can be executed against an IPA client, against
# an IPA server using "client" context, and ensure that tests are executed
# in upstream CI.

- name: Test netgroup with external members using client context, in client host.
ansible.builtin.import_playbook: test_netgroup_ext_member.yml
when: groups['ipaclients']
vars:
ipa_test_host: ipaclients

- name: Test netgroup with external members using client context, in server host.
ansible.builtin.import_playbook: test_netgroup_ext_member.yml
when: groups['ipaclients'] is not defined or not groups['ipaclients']
vars:
ipa_context: client
Loading

0 comments on commit ed98f82

Please sign in to comment.