Select Git revision
ipapermission.py
ipapermission.py 16.67 KiB
# -*- coding: utf-8 -*-
# Authors:
# Seth Kress <kresss@gmail.com>
#
# Copyright (C) 2020 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
"status": ["preview"],
}
DOCUMENTATION = """
---
module: ipapermission
short description: Manage FreeIPA permission
description: Manage FreeIPA permission and permission members
extends_documentation_fragment:
- ipamodule_base_docs
options:
name:
description: The permission name string.
required: true
aliases: ["cn"]
right:
description: Rights to grant
required: false
choices: ["read", "search", "compare", "write", "add", "delete", "all"]
type: list
aliases: ["ipapermright"]
attrs:
description: All attributes to which the permission applies
required: false
type: list
bindtype:
description: Bind rule type
required: false
choices: ["permission", "all", "anonymous"]
aliases: ["ipapermbindruletype"]
subtree:
description: Subtree to apply permissions to
required: false
aliases: ["ipapermlocation"]
filter:
description: Extra target filter
required: false
type: list
aliases: ["extratargetfilter"]
rawfilter:
description: All target filters
required: false
type: list
aliases: ["ipapermtargetfilter"]
target:
description: Optional DN to apply the permission to
required: false
aliases: ["ipapermtarget"]
targetto:
description: Optional DN subtree where an entry can be moved to
required: false
aliases: ["ipapermtargetto"]
targetfrom:
description: Optional DN subtree from where an entry can be moved
required: false
aliases: ["ipapermtargetfrom"]
memberof:
description: Target members of a group (sets memberOf targetfilter)
required: false
type: list
targetgroup:
description: User group to apply permissions to (sets target)
required: false
aliases: ["targetgroup"]
object_type:
description: Type of IPA object (sets subtree and objectClass targetfilter)
required: false
aliases: ["type"]
no_members:
description: Suppress processing of membership
required: false
type: bool
rename:
description: Rename the permission object
required: false
aliases: ["new_name"]
action:
description: Work on permission or member privilege level.
choices: ["permission", "member"]
default: permission
required: false
state:
description: The state to ensure.
choices: ["present", "absent", "renamed"]
default: present
required: true
"""
EXAMPLES = """
# Ensure permission NAME is present
- ipapermission:
name: manage-my-hostgroup
right: all
bindtype: permission
object_type: host
# Ensure permission NAME is absent
- ipapermission:
name: "Removed Permission Name"
state: absent
"""
RETURN = """
"""
from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa
def find_permission(module, name):
"""Find if a permission with the given name already exist."""
try:
_result = module.ipa_command("permission_show", name, {"all": True})
except Exception: # pylint: disable=broad-except
# An exception is raised if permission name is not found.
return None
else:
return _result["result"]
def gen_args(right, attrs, bindtype, subtree,
extra_target_filter, rawfilter, target,
targetto, targetfrom, memberof, targetgroup,
object_type, no_members, rename):
_args = {}
if right is not None:
_args["ipapermright"] = right
if attrs is not None:
_args["attrs"] = attrs
if bindtype is not None:
_args["ipapermbindruletype"] = bindtype
if subtree is not None:
_args["ipapermlocation"] = subtree
if extra_target_filter is not None:
_args["extratargetfilter"] = extra_target_filter
if rawfilter is not None:
_args["ipapermtargetfilter"] = rawfilter
if target is not None:
_args["ipapermtarget"] = target
if targetto is not None:
_args["ipapermtargetto"] = targetto
if targetfrom is not None:
_args["ipapermtargetfrom"] = targetfrom
if memberof is not None:
_args["memberof"] = memberof
if targetgroup is not None:
_args["targetgroup"] = targetgroup
if object_type is not None:
_args["type"] = object_type
if no_members is not None:
_args["no_members"] = no_members
if rename is not None:
_args["rename"] = rename
return _args
# pylint: disable=unused-argument
def result_handler(module, result, command, name, args, errors):
# Get all errors
# All "already a member" and "not a member" failures in the
# result are ignored. All others are reported.
for failed_item in result.get("failed", []):
failed = result["failed"][failed_item]
for member_type in failed:
for member, failure in failed[member_type]:
if "already a member" in failure \
or "not a member" in failure:
continue
errors.append("%s: %s %s: %s" % (
command, member_type, member, failure))
def main():
ansible_module = IPAAnsibleModule(
argument_spec=dict(
# general
name=dict(type="list", aliases=["cn"],
default=None, required=True),
# present
right=dict(type="list", aliases=["ipapermright"], default=None,
required=False,
choices=["read", "search", "compare", "write", "add",
"delete", "all"]),
attrs=dict(type="list", default=None, required=False),
# Note: bindtype has a default of permission for Adds.
bindtype=dict(type="str", aliases=["ipapermbindruletype"],
default=None, require=False, choices=["permission",
"all", "anonymous", "self"]),
subtree=dict(type="str", aliases=["ipapermlocation"], default=None,
required=False),
extra_target_filter=dict(type="list", aliases=["filter",
"extratargetfilter"], default=None,
required=False),
rawfilter=dict(type="list", aliases=["ipapermtargetfilter"],
default=None, required=False),
target=dict(type="str", aliases=["ipapermtarget"], default=None,
required=False),
targetto=dict(type="str", aliases=["ipapermtargetto"],
default=None, required=False),
targetfrom=dict(type="str", aliases=["ipapermtargetfrom"],
default=None, required=False),
memberof=dict(type="list", default=None, required=False),
targetgroup=dict(type="str", default=None, required=False),
object_type=dict(type="str", aliases=["type"], default=None,
required=False),
no_members=dict(type=bool, default=None, require=False),
rename=dict(type="str", default=None, required=False,
aliases=["new_name"]),
action=dict(type="str", default="permission",
choices=["member", "permission"]),
# state
state=dict(type="str", default="present",
choices=["present", "absent", "renamed"]),
),
supports_check_mode=True,
)
ansible_module._ansible_debug = True
# Get parameters
# general
names = ansible_module.params_get("name")
# present
right = ansible_module.params_get("right")
attrs = ansible_module.params_get("attrs")
bindtype = ansible_module.params_get("bindtype")
subtree = ansible_module.params_get("subtree")
extra_target_filter = ansible_module.params_get("extra_target_filter")
rawfilter = ansible_module.params_get("rawfilter")
target = ansible_module.params_get("target")
targetto = ansible_module.params_get("targetto")
targetfrom = ansible_module.params_get("targetfrom")
memberof = ansible_module.params_get("memberof")
targetgroup = ansible_module.params_get("targetgroup")
object_type = ansible_module.params_get("object_type")
no_members = ansible_module.params_get("no_members")
rename = ansible_module.params_get("rename")
action = ansible_module.params_get("action")
# state
state = ansible_module.params_get("state")
# Check parameters
invalid = []
if state == "present":
if len(names) != 1:
ansible_module.fail_json(
msg="Only one permission can be added at a time.")
if action == "member":
invalid = ["bindtype", "target", "targetto", "targetfrom",
"subtree", "targetgroup", "object_type", "rename"]
else:
invalid = ["rename"]
if state == "renamed":
if len(names) != 1:
ansible_module.fail_json(
msg="Only one permission can be renamed at a time.")
if action == "member":
ansible_module.fail_json(
msg="Member action can not be used with state 'renamed'")
invalid = ["right", "attrs", "bindtype", "subtree",
"extra_target_filter", "rawfilter", "target", "targetto",
"targetfrom", "memberof", "targetgroup", "object_type",
"no_members"]
if state == "absent":
if len(names) < 1:
ansible_module.fail_json(msg="No name given.")
invalid = ["bindtype", "subtree", "target", "targetto",
"targetfrom", "targetgroup", "object_type",
"no_members", "rename"]
if action != "member":
invalid += ["right", "attrs", "memberof",
"extra_target_filter", "rawfilter"]
ansible_module.params_fail_used_invalid(invalid, state, action)
if bindtype == "self" and ansible_module.ipa_check_version("<", "4.8.7"):
ansible_module.fail_json(
msg="Bindtype 'self' is not supported by your IPA version.")
if all([extra_target_filter, rawfilter]):
ansible_module.fail_json(
msg="Cannot specify target filter and extra target filter "
"simultaneously.")
# Init
changed = False
exit_args = {}
# Connect to IPA API
with ansible_module.ipa_connect():
commands = []
for name in names:
# Make sure permission exists
res_find = find_permission(ansible_module, name)
# Create command
if state == "present":
# Generate args
args = gen_args(right, attrs, bindtype, subtree,
extra_target_filter, rawfilter, target,
targetto, targetfrom, memberof, targetgroup,
object_type, no_members, rename)
if action == "permission":
# Found the permission
if res_find is not None:
# For all settings is args, check if there are
# different settings in the find result.
# If yes: modify
if not compare_args_ipa(ansible_module, args,
res_find):
commands.append([name, "permission_mod", args])
else:
commands.append([name, "permission_add", args])
elif action == "member":
if res_find is None:
ansible_module.fail_json(
msg="No permission '%s'" % name)
member_attrs = {}
check_members = {
"attrs": attrs,
"memberof": memberof,
"ipapermright": right,
"ipapermtargetfilter": rawfilter,
"extratargetfilter": extra_target_filter,
# subtree member management is currently disabled.
# "ipapermlocation": subtree,
}
for _member, _member_change in check_members.items():
if _member_change is not None:
_res_list = res_find[_member]
# if running in a client context, data may be
# returned as a tuple instead of a list.
if isinstance(_res_list, tuple):
_res_list = list(_res_list)
_new_set = set(_res_list + _member_change)
if _new_set != set(_res_list):
member_attrs[_member] = list(_new_set)
if member_attrs:
commands.append([name, "permission_mod", member_attrs])
else:
ansible_module.fail_json(
msg="Unknown action '%s'" % action)
elif state == "renamed":
if action == "permission":
# Generate args
# Note: Only valid arg for rename is rename.
args = gen_args(right, attrs, bindtype, subtree,
extra_target_filter, rawfilter, target,
targetto, targetfrom, memberof,
targetgroup, object_type, no_members,
rename)
# Found the permission
if res_find is not None:
# For all settings is args, check if there are
# different settings in the find result.
# If yes: modify
if not compare_args_ipa(ansible_module, args,
res_find):
commands.append([name, "permission_mod", args])
else:
ansible_module.fail_json(
msg="Permission not found, cannot rename")
else:
ansible_module.fail_json(
msg="Unknown action '%s'" % action)
elif state == "absent":
if action == "permission":
if res_find is not None:
commands.append([name, "permission_del", {}])
elif action == "member":
if res_find is None:
ansible_module.fail_json(
msg="No permission '%s'" % name)
member_attrs = {}
check_members = {
"attrs": attrs,
"memberof": memberof,
"ipapermright": right,
"ipapermtargetfilter": rawfilter,
"extratargetfilter": extra_target_filter,
# subtree member management is currently disabled.
# "ipapermlocation": subtree,
}
for _member, _member_change in check_members.items():
if _member_change is not None:
_res_set = set(res_find[_member])
_new_set = _res_set - set(_member_change)
if _new_set != _res_set:
member_attrs[_member] = list(_new_set)
if member_attrs:
commands.append([name, "permission_mod", member_attrs])
else:
ansible_module.fail_json(msg="Unknown state '%s'" % state)
# Execute commands
changed = ansible_module.execute_ipa_commands(commands, result_handler)
# Done
ansible_module.exit_json(changed=changed, **exit_args)
if __name__ == "__main__":
main()