Skip to content

Commit

Permalink
ipauser: Implement state:quers using IPAAnsibleModule.execute_query
Browse files Browse the repository at this point in the history
The query_param parameter has been added, together with the dict
query_param_settings. Also a new convert_result and user_find function
has been added.
  • Loading branch information
t-woerner committed Aug 25, 2022
1 parent f28e2d0 commit 54665c6
Show file tree
Hide file tree
Showing 2 changed files with 309 additions and 9 deletions.
145 changes: 136 additions & 9 deletions plugins/modules/ipauser.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,9 @@
default: "always"
choices: ["always", "on_create"]
required: false
query_param:
description: The fields to query with state=query
required: false
action:
description: Work on user or member level
default: "user"
Expand All @@ -393,7 +396,8 @@
default: present
choices: ["present", "absent",
"enabled", "disabled",
"unlocked", "undeleted"]
"unlocked", "undeleted",
"query"]
author:
- Thomas Woerner
"""
Expand Down Expand Up @@ -481,7 +485,7 @@
unicode = str


def find_user(module, name):
def user_show(module, name):
_args = {
"all": True,
}
Expand All @@ -501,6 +505,49 @@ def find_user(module, name):
return _result


def convert_result(res):
_res = {}
for key in res:
if key in ["manager", "krbprincipalname", "ipacertmapdata"]:
_res[key] = [to_text(x) for x in (res.get(key) or [])]
elif key == "usercertificate":
_res[key] = [encode_certificate(x) for x in (res.get(key) or [])]
elif isinstance(res[key], list) and len(res[key]) == 1:
# All single value parameters should not be lists
# This does not apply to manager, krbprincipalname,
# usercertificate and ipacertmapdata
_res[key] = to_text(res[key][0])
elif key in ["uidNumber", "gidNumber"]:
_res[key] = int(res[key])
else:
_res[key] = to_text(res[key])
return _res


def user_find(module, name, sizelimit=None, timelimit=None):
_args = {"all": True}

if sizelimit is not None:
_args["sizelimit"] = sizelimit
if timelimit is not None:
_args["timelimit"] = timelimit

try:
if name:
_args["uid"] = name
_result = module.ipa_command_no_name("user_find", _args).get("result")
if _result:
if name:
_result = convert_result(_result[0])
else:
_result = [convert_result(res) for res in _result]

except ipalib_errors.NotFound:
return None
else:
return _result


def gen_args(first, last, fullname, displayname, initials, homedir, shell,
email, principalexpiration, passwordexpiration, password,
random, uid, gid, city, userstate, postalcode, phone, mobile,
Expand Down Expand Up @@ -618,6 +665,15 @@ def check_parameters( # pylint: disable=unused-argument
"certificate", "certmapdata",
])

if state == "query":
invalid.append("users")

if action == "member":
module.fail_json(
msg="Query is not possible with action=member")
else:
invalid.append("query_param")

if state != "absent" and preserve is not None:
module.fail_json(
msg="Preserve is only possible for state=absent")
Expand Down Expand Up @@ -742,6 +798,59 @@ def exception_handler(module, ex, errors, exit_args, one_name):
return False


query_param_settings = {
# password, randompassword and krbprincipalkey may not be in the returned
# information even in server context.
"ALL": [
"dn", "objectclass", "ipauniqueid", "ipantsecurityidentifier", "name",
"first", "last", "fullname", "displayname", "initials", "homedir",
"shell", "email", "principalexpiration", "passwordexpiration", "uid",
"gid", "city", "userstate", "postalcode", "phone", "mobile", "pager",
"fax", "orgunit", "title", "carlicense", "sshpubkey", "userauthtype",
"userclass", "radius", "radiususer", "departmentnumber",
"employeenumber", "employeetype", "preferredlanguage", "manager",
"principal", "certificate", "certmapdata", "gecos", "krblastpwdchange",
"krblastadminunlock", "krbextradata", "krbticketflags",
"krbloginfailedcount", "krblastsuccessfulauth", "has_password",
"has_keytab", "preserved", "memberof_group", "disabled"
],
"BASE": [
"name", "first", "last", "shell", "principal", "uid", "gid",
"disabled"
],
"mapping": {
"name": "uid",
"first": "givenname",
"last": "sn",
"fullname": "cn",
"homedir": "homedirectory",
"shell": "loginshell",
"email": "mail",
"principalexpiration": "krbprincipalexpiration",
"passwordexpiration": "krbpasswordexpiration",
"uid": "uidnumber",
"gid": "gidnumber",
"city": "l",
"userstate": "st",
"postalcode": "postalcode",
"phone": "telephonenumber",
"mobile": "mobile",
"pager": "pager",
"fax": "facsimiletelephonenumber",
"orgunit": "ou",
"sshpubkey": "ipasshpubkey",
"userauthtype": "ipauserauthtype",
"radius": "ipatokenradiusconfiglink",
"radiususer": "ipatokenradiususername",
"preferredlanguage": "preferredlanguage",
"principal": "krbprincipalname",
"certificate": "usercertificate",
"certmapdata": "ipacertmapdata",
"disabled": "nsaccountock"
}
}


def main():
user_spec = dict(
# present
Expand Down Expand Up @@ -833,18 +942,25 @@ def main():
update_password=dict(type='str', default=None, no_log=False,
choices=['always', 'on_create']),

# query
query_param=dict(type="list", default=None,
choices=["ALL", "BASE"].extend(
query_param_settings["ALL"]),
required=False),

# general
action=dict(type="str", default="user",
choices=["member", "user"]),
state=dict(type="str", default="present",
choices=["present", "absent", "enabled", "disabled",
"unlocked", "undeleted"]),
"unlocked", "undeleted", "query"]),

# Add user specific parameters for simple use case
**user_spec
),
mutually_exclusive=[["name", "users"]],
required_one_of=[["name", "users"]],
# Required one of [["name", "users"]] has been removed as there is
# an extra test below and it is not working with state=query
supports_check_mode=True,
)

Expand Down Expand Up @@ -911,15 +1027,19 @@ def main():
preserve = ansible_module.params_get("preserve")
# mod
update_password = ansible_module.params_get("update_password")
# query
query_param = ansible_module.params_get("query_param")

# general
action = ansible_module.params_get("action")
state = ansible_module.params_get("state")

# Check parameters

if (names is None or len(names) < 1) and \
(users is None or len(users) < 1):
ansible_module.fail_json(msg="One of name and users is required")
if state != "query":
if (names is None or len(names) < 1) and \
(users is None or len(users) < 1):
ansible_module.fail_json(msg="One of name and users is required")

if state == "present":
if names is not None and len(names) != 1:
Expand Down Expand Up @@ -949,6 +1069,13 @@ def main():
# Connect to IPA API
with ansible_module.ipa_connect():

if state == "query":
exit_args = ansible_module.execute_query(
names, "users", "uid", query_param, user_find,
query_param_settings)

ansible_module.exit_json(changed=False, user=exit_args)

# Check version specific settings

server_realm = ansible_module.ipa_get_realm()
Expand Down Expand Up @@ -1076,7 +1203,7 @@ def main():
"your IPA version")

# Make sure user exists
res_find = find_user(ansible_module, name)
res_find = user_show(ansible_module, name)

# Create command
if state == "present":
Expand Down Expand Up @@ -1136,7 +1263,7 @@ def main():
principal_add, principal_del = gen_add_del_lists(
principal, res_find.get("krbprincipalname"))
# Principals are not returned as utf8 for IPA using
# python2 using user_find, therefore we need to
# python2 using user_show, therefore we need to
# convert the principals that we should remove.
principal_del = [to_text(x) for x in principal_del]

Expand Down
Loading

0 comments on commit 54665c6

Please sign in to comment.