mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-07-30 11:24:50 +00:00
ipauser: Fix idempotence issue when using 'preserved'.
When trying to ensure 'state: absent' with 'preserved: yes' in ipauser, after the first execution the playbook would fail with "user is already present". Similar idempotence issue would happen when 'state: undelete' was used. This PR fixes both issues, and improve tests for the states where user is preserved, enabled and disabled. The 'find_user' function now uses IPA API 'user_show' instead of 'user_find' so that only the requested user is actually returned.
This commit is contained in:
@@ -474,41 +474,31 @@ user:
|
|||||||
|
|
||||||
from ansible.module_utils.ansible_freeipa_module import \
|
from ansible.module_utils.ansible_freeipa_module import \
|
||||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, date_format, \
|
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, date_format, \
|
||||||
encode_certificate, load_cert_from_str, DN_x500_text, to_text
|
encode_certificate, load_cert_from_str, DN_x500_text, to_text, \
|
||||||
|
ipalib_errors
|
||||||
from ansible.module_utils import six
|
from ansible.module_utils import six
|
||||||
if six.PY3:
|
if six.PY3:
|
||||||
unicode = str
|
unicode = str
|
||||||
|
|
||||||
|
|
||||||
def find_user(module, name, preserved=False):
|
def find_user(module, name):
|
||||||
_args = {
|
_args = {
|
||||||
"all": True,
|
"all": True,
|
||||||
"uid": name,
|
|
||||||
}
|
}
|
||||||
if preserved:
|
|
||||||
_args["preserved"] = preserved
|
|
||||||
|
|
||||||
_result = module.ipa_command("user_find", name, _args)
|
try:
|
||||||
|
_result = module.ipa_command("user_show", name, _args).get("result")
|
||||||
|
except ipalib_errors.NotFound:
|
||||||
|
return None
|
||||||
|
|
||||||
if len(_result["result"]) > 1:
|
# Transform each principal to a string
|
||||||
module.fail_json(
|
_result["krbprincipalname"] = [
|
||||||
msg="There is more than one user '%s'" % (name))
|
to_text(x) for x in (_result.get("krbprincipalname") or [])
|
||||||
elif len(_result["result"]) == 1:
|
]
|
||||||
# Transform each principal to a string
|
_result["usercertificate"] = [
|
||||||
_result = _result["result"][0]
|
encode_certificate(x) for x in (_result.get("usercertificate") or [])
|
||||||
if "krbprincipalname" in _result \
|
]
|
||||||
and _result["krbprincipalname"] is not None:
|
return _result
|
||||||
_list = []
|
|
||||||
for x in _result["krbprincipalname"]:
|
|
||||||
_list.append(str(x))
|
|
||||||
_result["krbprincipalname"] = _list
|
|
||||||
certs = _result.get("usercertificate")
|
|
||||||
if certs is not None:
|
|
||||||
_result["usercertificate"] = [encode_certificate(x)
|
|
||||||
for x in certs]
|
|
||||||
return _result
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def gen_args(first, last, fullname, displayname, initials, homedir, shell,
|
def gen_args(first, last, fullname, displayname, initials, homedir, shell,
|
||||||
@@ -1085,12 +1075,6 @@ def main():
|
|||||||
|
|
||||||
# Make sure user exists
|
# Make sure user exists
|
||||||
res_find = find_user(ansible_module, name)
|
res_find = find_user(ansible_module, name)
|
||||||
# Also search for preserved user if the user could not be found
|
|
||||||
if res_find is None:
|
|
||||||
res_find_preserved = find_user(ansible_module, name,
|
|
||||||
preserved=True)
|
|
||||||
else:
|
|
||||||
res_find_preserved = None
|
|
||||||
|
|
||||||
# Create command
|
# Create command
|
||||||
if state == "present":
|
if state == "present":
|
||||||
@@ -1104,10 +1088,6 @@ def main():
|
|||||||
departmentnumber, employeenumber, employeetype,
|
departmentnumber, employeenumber, employeetype,
|
||||||
preferredlanguage, noprivate, nomembers)
|
preferredlanguage, noprivate, nomembers)
|
||||||
|
|
||||||
# Also check preserved users
|
|
||||||
if res_find is None and res_find_preserved is not None:
|
|
||||||
res_find = res_find_preserved
|
|
||||||
|
|
||||||
if action == "user":
|
if action == "user":
|
||||||
# Found the user
|
# Found the user
|
||||||
if res_find is not None:
|
if res_find is not None:
|
||||||
@@ -1310,16 +1290,16 @@ def main():
|
|||||||
gen_certmapdata_args(_data)])
|
gen_certmapdata_args(_data)])
|
||||||
|
|
||||||
elif state == "absent":
|
elif state == "absent":
|
||||||
# Also check preserved users
|
|
||||||
if res_find is None and res_find_preserved is not None:
|
|
||||||
res_find = res_find_preserved
|
|
||||||
|
|
||||||
if action == "user":
|
if action == "user":
|
||||||
if res_find is not None:
|
if res_find is not None:
|
||||||
args = {}
|
args = {}
|
||||||
if preserve is not None:
|
if preserve is not None:
|
||||||
args["preserve"] = preserve
|
args["preserve"] = preserve
|
||||||
commands.append([name, "user_del", args])
|
if (
|
||||||
|
not res_find.get("preserved", False)
|
||||||
|
or not args.get("preserve", False)
|
||||||
|
):
|
||||||
|
commands.append([name, "user_del", args])
|
||||||
elif action == "member":
|
elif action == "member":
|
||||||
if res_find is None:
|
if res_find is None:
|
||||||
ansible_module.fail_json(
|
ansible_module.fail_json(
|
||||||
@@ -1370,8 +1350,9 @@ def main():
|
|||||||
commands.append([name, "user_remove_certmapdata",
|
commands.append([name, "user_remove_certmapdata",
|
||||||
gen_certmapdata_args(_data)])
|
gen_certmapdata_args(_data)])
|
||||||
elif state == "undeleted":
|
elif state == "undeleted":
|
||||||
if res_find_preserved is not None:
|
if res_find is not None:
|
||||||
commands.append([name, "user_undel", {}])
|
if res_find.get("preserved", False):
|
||||||
|
commands.append([name, "user_undel", {}])
|
||||||
else:
|
else:
|
||||||
raise ValueError("No preserved user '%s'" % name)
|
raise ValueError("No preserved user '%s'" % name)
|
||||||
|
|
||||||
|
|||||||
@@ -249,6 +249,16 @@
|
|||||||
register: result
|
register: result
|
||||||
failed_when: not result.changed or result.failed
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: User pinky absent and preserved, again
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
name: pinky
|
||||||
|
preserve: yes
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
- name: User pinky undeleted (preserved before)
|
- name: User pinky undeleted (preserved before)
|
||||||
ipauser:
|
ipauser:
|
||||||
ipaadmin_password: SomeADMINpassword
|
ipaadmin_password: SomeADMINpassword
|
||||||
@@ -258,6 +268,15 @@
|
|||||||
register: result
|
register: result
|
||||||
failed_when: not result.changed or result.failed
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: User pinky undeleted (preserved before), again
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
name: pinky
|
||||||
|
state: undeleted
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
- name: Users pinky disabled
|
- name: Users pinky disabled
|
||||||
ipauser:
|
ipauser:
|
||||||
ipaadmin_password: SomeADMINpassword
|
ipaadmin_password: SomeADMINpassword
|
||||||
@@ -267,6 +286,15 @@
|
|||||||
register: result
|
register: result
|
||||||
failed_when: not result.changed or result.failed
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Users pinky disabled, again
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
name: pinky
|
||||||
|
state: disabled
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
- name: User pinky enabled
|
- name: User pinky enabled
|
||||||
ipauser:
|
ipauser:
|
||||||
ipaadmin_password: SomeADMINpassword
|
ipaadmin_password: SomeADMINpassword
|
||||||
@@ -276,6 +304,44 @@
|
|||||||
register: result
|
register: result
|
||||||
failed_when: not result.changed or result.failed
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: User pinky enabled, again
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
name: pinky
|
||||||
|
state: enabled
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
|
- name: User pinky absent and preserved for future exclusion.
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
name: pinky
|
||||||
|
preserve: yes
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: User pinky absent
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
name: pinky
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: User pinky absent and preserved, when already absent
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
name: pinky
|
||||||
|
preserve: yes
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
- name: Remove test users
|
- name: Remove test users
|
||||||
ipauser:
|
ipauser:
|
||||||
ipaadmin_password: SomeADMINpassword
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
|||||||
@@ -369,6 +369,15 @@
|
|||||||
register: result
|
register: result
|
||||||
failed_when: not result.changed or result.failed
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: User pinky absent and preserved, again
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: pinky
|
||||||
|
preserve: yes
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
- name: User pinky undeleted (preserved before)
|
- name: User pinky undeleted (preserved before)
|
||||||
ipauser:
|
ipauser:
|
||||||
ipaadmin_password: SomeADMINpassword
|
ipaadmin_password: SomeADMINpassword
|
||||||
@@ -377,6 +386,14 @@
|
|||||||
register: result
|
register: result
|
||||||
failed_when: not result.changed or result.failed
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: User pinky undeleted (preserved before), again
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: pinky
|
||||||
|
state: undeleted
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
- name: Users pinky disabled
|
- name: Users pinky disabled
|
||||||
ipauser:
|
ipauser:
|
||||||
ipaadmin_password: SomeADMINpassword
|
ipaadmin_password: SomeADMINpassword
|
||||||
@@ -385,6 +402,14 @@
|
|||||||
register: result
|
register: result
|
||||||
failed_when: not result.changed or result.failed
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: Users pinky disabled, again
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: pinky
|
||||||
|
state: disabled
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
- name: User pinky enabled
|
- name: User pinky enabled
|
||||||
ipauser:
|
ipauser:
|
||||||
ipaadmin_password: SomeADMINpassword
|
ipaadmin_password: SomeADMINpassword
|
||||||
@@ -393,6 +418,43 @@
|
|||||||
register: result
|
register: result
|
||||||
failed_when: not result.changed or result.failed
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: User pinky enabled, again
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
name: pinky
|
||||||
|
state: enabled
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
|
- name: User pinky absent and preserved for future exclusion.
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
name: pinky
|
||||||
|
preserve: yes
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: User pinky absent
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
name: pinky
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
failed_when: not result.changed or result.failed
|
||||||
|
|
||||||
|
- name: User pinky absent and preserved, when already absent
|
||||||
|
ipauser:
|
||||||
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||||
|
name: pinky
|
||||||
|
preserve: yes
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
failed_when: result.changed or result.failed
|
||||||
|
|
||||||
- name: Remove test users
|
- name: Remove test users
|
||||||
ipauser:
|
ipauser:
|
||||||
ipaadmin_password: SomeADMINpassword
|
ipaadmin_password: SomeADMINpassword
|
||||||
|
|||||||
Reference in New Issue
Block a user