Skip to content

Commit

Permalink
ipagroup: 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. The existing find_group function has been
transformed into user_show to get the result for a single user and
new convert_result and user_find function have been added.
  • Loading branch information
t-woerner committed Sep 28, 2023
1 parent 15b9649 commit 54288e5
Show file tree
Hide file tree
Showing 4 changed files with 379 additions and 29 deletions.
10 changes: 6 additions & 4 deletions plugins/module_utils/ansible_freeipa_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -1358,7 +1358,7 @@ def exception_handler(module, ex, exit_args, one_name):
return changed

def execute_query(self, names, prefix, name_ipa_param,
query_param, query_command, query_param_settings):
query_param, find_command, query_param_settings):
"""
Execute query state.
Expand All @@ -1381,7 +1381,7 @@ def execute_query(self, names, prefix, name_ipa_param,
mapping of the default module paramter name to IPA option name
if it is not the same.
Example: "uid" for user name of the user commands.
query_command: The Query function
find_command: The find function
This is a module function that returns the structure(s) from
the show or find command.
Expand Down Expand Up @@ -1421,13 +1421,15 @@ def store_params(exit_args, name, prefix, name_ipa_param, result,
if names and isinstance(names, list):
with_name = len(names) > 1
for name in names:
result = query_command(self, name)
result = find_command(self, name,
pkey_only=query_param is None)
if result:
store_params(exit_args, name if with_name else None,
prefix, name_ipa_param, result,
query_param)
else:
results = query_command(self, None)
results = find_command(self, None,
pkey_only=query_param is None)
if results is not None:
for result in results:
name = result[name_ipa_param]
Expand Down
138 changes: 116 additions & 22 deletions plugins/modules/ipagroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@
required: false
type: list
elements: str
query_param:
description: The fields to query with state=query
required: false
action:
description: Work on group or member level
type: str
Expand All @@ -202,7 +205,8 @@
description: State to ensure
type: str
default: present
choices: ["present", "absent"]
choices: ["present", "absent",
"query"]
author:
- Thomas Woerner (@t-woerner)
"""
Expand Down Expand Up @@ -310,7 +314,7 @@
from ansible.module_utils._text import to_text
from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \
gen_add_list, gen_intersection_list, api_check_param
gen_add_list, gen_intersection_list, api_check_param, ipalib_errors
from ansible.module_utils import six
if six.PY3:
unicode = str
Expand All @@ -327,28 +331,67 @@
"deepcopy" in baseldap.LDAPObject.__json__.__code__.co_names


def find_group(module, name):
def group_show(module, name):
_args = {
"all": True,
"cn": name,
}

_result = module.ipa_command("group_find", name, _args)
try:
_result = module.ipa_command("group_show", name, _args).get("result")
except ipalib_errors.NotFound:
return None

# The returned services are of type ipapython.kerberos.Principal,
# also services are not case sensitive. Therefore services are
# converted to lowercase strings to be able to do the comparison.
if "member_service" in _result:
_result["member_service"] = \
[to_text(svc).lower() for svc in _result["member_service"]]
return _result


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


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

if name:
_args["cn"] = name
if pkey_only:
_args["pkey_only"] = True
if sizelimit is not None:
_args["sizelimit"] = sizelimit
if timelimit is not None:
_args["timelimit"] = timelimit

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

if len(_result["result"]) > 1:
module.fail_json(
msg="There is more than one group '%s'" % (name))
elif len(_result["result"]) == 1:
_res = _result["result"][0]
# The returned services are of type ipapython.kerberos.Principal,
# also services are not case sensitive. Therefore services are
# converted to lowercase strings to be able to do the comparison.
if "member_service" in _res:
_res["member_service"] = \
[to_text(svc).lower() for svc in _res["member_service"]]
return _res
except ipalib_errors.NotFound:
return None

return None
return _result


def gen_args(description, gid, nomembers):
Expand Down Expand Up @@ -392,6 +435,11 @@ def check_parameters(module, state, action):
if action == "group":
invalid.extend(["user", "group", "service", "externalmember"])

if state == "query":
invalid.append("groups")
else:
invalid.append("query_param")

module.params_fail_used_invalid(invalid, state, action)


Expand Down Expand Up @@ -422,6 +470,31 @@ def check_objectclass_args(module, res_find, posix, external):
"`non-posix`.")


query_param_settings = {
"ALL": [
"dn", "objectclass", "ipauniqueid", "ipantsecurityidentifier",
"name",
"description",
"gid",
"nomembers",
"user", "group", "service", "external",
"idoverrideuser"
],
"BASE": [
"name", "description", "gid"
],
"mapping": {
"name": "cn",
"gid": "gidnumber",
"user": "member_user",
"group": "member_group",
"service": "member_service",
"externalmember": "member_external",
"idoverrideuser": "member_idoverrideuser",
}
}


def main():
group_spec = dict(
# present
Expand Down Expand Up @@ -466,11 +539,16 @@ def main():
),
elements='dict',
required=False),
# query
query_param=dict(type="list", default=None,
choices=["ALL", "BASE"].extend(
query_param_settings["ALL"]),
required=False),
# general
action=dict(type="str", default="group",
choices=["member", "group"]),
state=dict(type="str", default="present",
choices=["present", "absent"]),
choices=["present", "absent", "query"]),

# Add group specific parameters for simple use case
**group_spec
Expand All @@ -479,7 +557,7 @@ def main():
# same time
mutually_exclusive=[['posix', 'nonposix', 'external'],
["name", "groups"]],
required_one_of=[["name", "groups"]],
# required_one_of=[["name", "groups"]] is handled below
supports_check_mode=True,
)

Expand All @@ -506,13 +584,17 @@ def main():
membermanager_user = ansible_module.params_get("membermanager_user")
membermanager_group = ansible_module.params_get("membermanager_group")
externalmember = ansible_module.params_get("externalmember")
# query
query_param = ansible_module.params_get("query_param")
# action
action = ansible_module.params_get("action")
# state
state = ansible_module.params_get("state")

# Check parameters

if (names is None or len(names) < 1) and \
if state != "query" and \
(names is None or len(names) < 1) and \
(groups is None or len(groups) < 1):
ansible_module.fail_json(msg="At least one name or groups is required")

Expand All @@ -521,6 +603,11 @@ def main():
ansible_module.fail_json(
msg="Only one group can be added at a time using 'name'.")

if state == "query":
if action == "member":
ansible_module.fail_json(
msg="Query is not possible with action=query")

check_parameters(ansible_module, state, action)

if external is False:
Expand Down Expand Up @@ -567,6 +654,13 @@ def main():
# Connect to IPA API
with ansible_module.ipa_connect(context=context):

if state == "query":
exit_args = ansible_module.execute_query(
names, "groups", "cn", query_param, group_find,
query_param_settings)

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

has_add_member_service = ansible_module.ipa_command_param_exists(
"group_add_member", "service")
if service is not None and not has_add_member_service:
Expand Down Expand Up @@ -643,7 +737,7 @@ def main():
repr(group_name))

# Make sure group exists
res_find = find_group(ansible_module, name)
res_find = group_show(ansible_module, name)

user_add, user_del = [], []
group_add, group_del = [], []
Expand Down
8 changes: 5 additions & 3 deletions plugins/modules/ipauser.py
Original file line number Diff line number Diff line change
Expand Up @@ -772,17 +772,19 @@ def convert_result(res):
return _res


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

if name:
_args["uid"] = name
if pkey_only:
_args["pkey_only"] = 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:
Expand Down
Loading

0 comments on commit 54288e5

Please sign in to comment.