From 62ccf350818ee84d07c5550f2a46f3744f6d7e9d Mon Sep 17 00:00:00 2001 From: Philippe Duveau Date: Tue, 17 Dec 2024 22:21:59 +0100 Subject: [PATCH 1/6] Add module ldap_inc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This module adds the ‘modify-increment’ capability corresponding to the extension implemented by OpenLdap described in RFC-4525. It can be used to increment an integer attribute and read it atomically. It is an help for posix userId definition while relying only on the directory server. --- plugins/modules/ldap_inc.py | 170 ++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 plugins/modules/ldap_inc.py diff --git a/plugins/modules/ldap_inc.py b/plugins/modules/ldap_inc.py new file mode 100644 index 00000000000..b60cb970d61 --- /dev/null +++ b/plugins/modules/ldap_inc.py @@ -0,0 +1,170 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024, Philippe Duveau +# Copyright (c) 2019, Maciej Delmanowski (ldap_attrs.py) +# Copyright (c) 2017, Alexander Korinek (ldap_attrs.py) +# Copyright (c) 2016, Peter Sagerson (ldap_attrs.py) +# Copyright (c) 2016, Jiri Tyr (ldap_attrs.py) +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +# The code of this module is derived from that of ldap_attrs.py + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r''' +--- +module: ldap_inc +short_description: Use the Modify-Increment ldap V3 feature to increment an attribute value +description: + - Atomically increments the value of an attribute and return its new value. +notes: + - This only deals with integer attribute of an existing entry. To modify attributes + of an entry, see M(community.general.ldap_attrs) or to add or remove whole entries, + see M(community.general.ldap_entry). + - The default authentication settings will attempt to use a SASL EXTERNAL + bind over a UNIX domain socket. If you need to use a simple bind to access + your server, pass the credentials in O(bind_dn) and O(bind_pw). +version_added: '2.17.6' +author: + - Philippe Duveau (@pduveau) +requirements: + - python-ldap +attributes: + check_mode: + support: full + diff_mode: + support: full +options: + attribute: + required: true + type: string + description: + - The attribute to increment. + increment: + required: false + type: int + default: 1 + description: + - The value of the increment to apply. +extends_documentation_fragment: + - community.general.ldap.documentation + +''' + + +EXAMPLES = r''' +- name: Increments uidNumber 1 Number for example.com + community.general.ldap_inc: + dn: "cn=uidNext,ou=unix-management,dc=example,dc=com" + attribute: "uidNumber" + increment: "1" + register: ldap_uidNumber_sequence + +- name: Modifies the user to define its identification number (uidNumber) when incrementation is successful. + community.general.ldap_attrs: + dn: "cn=john,ou=posix-users,dc=example,dc=com" + state: present + attributes: + - uidNumber: "{{ ldap_uidNumber_sequence.value }} + when: ldap_uidNumber_sequence.incremented +''' + + +RETURN = r''' +result: + description: + - attribute received the attributeType changed + - value receive the new value of the attribute in the specificied object + returned: success + type: int + sample: + - incremented: true + - attribute: "uidNumber" + - value: "2" +''' + +import traceback + +from ansible.module_utils.basic import AnsibleModule, missing_required_lib +from ansible.module_utils.common.text.converters import to_native, to_bytes +from ansible_collections.community.general.plugins.module_utils.ldap import LdapGeneric, gen_specs, ldap_required_together + +LDAP_IMP_ERR = None +try: + import ldap + from ldap.controls.readentry import ReadEntryControl + + HAS_LDAP = True +except ImportError: + LDAP_IMP_ERR = traceback.format_exc() + HAS_LDAP = False + + +class LdapInc(LdapGeneric): + def __init__(self, module): + LdapGeneric.__init__(self, module) + # Shortcuts + self.attr = self.module.params['attribute'] + self.increment = self.module.params['increment'] + + + def inc(self): + return [ (ldap.MOD_INCREMENT, self.attr, [to_bytes(str(self.increment))]) ] + + def serverControls(self): + return [ ldap.controls.readentry.PostReadControl(attrList=[ self.attr ]) ] + +def main(): + module = AnsibleModule( + argument_spec=gen_specs( + attribute=dict(type='str', required=True), + increment=dict(type='int', default=1, required=False), + ), + supports_check_mode=True, + required_together=ldap_required_together(), + ) + + if not HAS_LDAP: + module.fail_json(msg=missing_required_lib('python-ldap'), + exception=LDAP_IMP_ERR) + + # Instantiate the LdapAttr object + mod = LdapInc(module) + + changed = False + ret = "" + + try: + if mod.increment != 0: + changed = True + + if not module.check_mode: + _, _, _, resp_ctrls = mod.connection.modify_ext_s( + dn=mod.dn, + modlist=mod.inc(), + serverctrls=mod.serverControls(), + clientctrls=None) + if len(resp_ctrls) == 1: + ret = resp_ctrls[0].entry[mod.attr][0] + else: + if not module.check_mode: + result = mod.connection.search_ext_s( + base=mod.dn, + scope=ldap.SCOPE_BASE, + filterstr="(%s=*)" % mod.attr, + attrlist=[mod.attr]) + if len(result) == 1: + ret = result[0][1][mod.attr][0] + + except Exception as e: + module.fail_json(msg="Attribute action failed.", details=to_native(e)) + + module.exit_json(changed=changed, incremented=changed, attribute=mod.attr, value=ret) + + +if __name__ == '__main__': + main() From bc256aa44afbf80ca97a3eeb6c6bad1637ad0602 Mon Sep 17 00:00:00 2001 From: Philippe Duveau Date: Tue, 17 Dec 2024 22:31:06 +0100 Subject: [PATCH 2/6] Update BOTMETA.yml to apply contribution rules --- .github/BOTMETA.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index 6896106906c..846a8906de6 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -850,6 +850,8 @@ files: maintainers: drybjed jtyr noles $modules/ldap_entry.py: maintainers: jtyr + $modules/ldap_inc.py: + maintainer: pduveau $modules/ldap_passwd.py: maintainers: KellerFuchs jtyr $modules/ldap_search.py: From 305fa93e604bdae03b8b25c3b9e834cdacb5e953 Mon Sep 17 00:00:00 2001 From: Philippe Duveau Date: Tue, 17 Dec 2024 23:19:55 +0100 Subject: [PATCH 3/6] Typo adjustment --- .github/BOTMETA.yml | 2 +- plugins/modules/ldap_inc.py | 43 ++++++++++++++++++------------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index 846a8906de6..c6f42255ef5 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -851,7 +851,7 @@ files: $modules/ldap_entry.py: maintainers: jtyr $modules/ldap_inc.py: - maintainer: pduveau + maintainers: pduveau $modules/ldap_passwd.py: maintainers: KellerFuchs jtyr $modules/ldap_search.py: diff --git a/plugins/modules/ldap_inc.py b/plugins/modules/ldap_inc.py index b60cb970d61..7f468357074 100644 --- a/plugins/modules/ldap_inc.py +++ b/plugins/modules/ldap_inc.py @@ -18,22 +18,22 @@ DOCUMENTATION = r''' --- module: ldap_inc -short_description: Use the Modify-Increment ldap V3 feature to increment an attribute value +short_description: Use the Modify-Increment ldap V3 feature to increment an attribute value description: - Atomically increments the value of an attribute and return its new value. notes: - This only deals with integer attribute of an existing entry. To modify attributes - of an entry, see M(community.general.ldap_attrs) or to add or remove whole entries, + of an entry, see M(community.general.ldap_attrs) or to add or remove whole entries, see M(community.general.ldap_entry). - The default authentication settings will attempt to use a SASL EXTERNAL - bind over a UNIX domain socket. If you need to use a simple bind to access + bind over a UNIX domain socket. If you need to use a simple bind to access your server, pass the credentials in O(bind_dn) and O(bind_pw). version_added: '2.17.6' author: - Philippe Duveau (@pduveau) requirements: - python-ldap -attributes: +attribute: check_mode: support: full diff_mode: @@ -41,7 +41,7 @@ options: attribute: required: true - type: string + type: str description: - The attribute to increment. increment: @@ -56,7 +56,7 @@ ''' -EXAMPLES = r''' +EXAMPLES = r""" - name: Increments uidNumber 1 Number for example.com community.general.ldap_inc: dn: "cn=uidNext,ou=unix-management,dc=example,dc=com" @@ -68,13 +68,13 @@ community.general.ldap_attrs: dn: "cn=john,ou=posix-users,dc=example,dc=com" state: present - attributes: + attributes: - uidNumber: "{{ ldap_uidNumber_sequence.value }} when: ldap_uidNumber_sequence.incremented -''' +""" -RETURN = r''' +RETURN = r""" result: description: - attribute received the attributeType changed @@ -85,7 +85,7 @@ - incremented: true - attribute: "uidNumber" - value: "2" -''' +""" import traceback @@ -111,13 +111,12 @@ def __init__(self, module): self.attr = self.module.params['attribute'] self.increment = self.module.params['increment'] - def inc(self): - return [ (ldap.MOD_INCREMENT, self.attr, [to_bytes(str(self.increment))]) ] + return [(ldap.MOD_INCREMENT, self.attr, [to_bytes(str(self.increment))])] def serverControls(self): - return [ ldap.controls.readentry.PostReadControl(attrList=[ self.attr ]) ] - + return [ldap.controls.readentry.PostReadControl(attrList=[ self.attr ])] + def main(): module = AnsibleModule( argument_spec=gen_specs( @@ -144,19 +143,19 @@ def main(): if not module.check_mode: _, _, _, resp_ctrls = mod.connection.modify_ext_s( - dn=mod.dn, - modlist=mod.inc(), - serverctrls=mod.serverControls(), - clientctrls=None) + dn=mod.dn, + modlist=mod.inc(), + serverctrls=mod.serverControls(), + clientctrls=None) if len(resp_ctrls) == 1: ret = resp_ctrls[0].entry[mod.attr][0] else: if not module.check_mode: result = mod.connection.search_ext_s( - base=mod.dn, - scope=ldap.SCOPE_BASE, - filterstr="(%s=*)" % mod.attr, - attrlist=[mod.attr]) + base=mod.dn, + scope=ldap.SCOPE_BASE, + filterstr="(%s=*)" % mod.attr, + attrlist=[mod.attr]) if len(result) == 1: ret = result[0][1][mod.attr][0] From dd523132500af702ed61629ea7b39087a78c4a85 Mon Sep 17 00:00:00 2001 From: Philippe Duveau Date: Wed, 18 Dec 2024 00:55:23 +0100 Subject: [PATCH 4/6] Fix sanity check errors and warnings --- plugins/modules/ldap_inc.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/modules/ldap_inc.py b/plugins/modules/ldap_inc.py index 7f468357074..c79b643953d 100644 --- a/plugins/modules/ldap_inc.py +++ b/plugins/modules/ldap_inc.py @@ -28,12 +28,11 @@ - The default authentication settings will attempt to use a SASL EXTERNAL bind over a UNIX domain socket. If you need to use a simple bind to access your server, pass the credentials in O(bind_dn) and O(bind_pw). -version_added: '2.17.6' author: - Philippe Duveau (@pduveau) requirements: - python-ldap -attribute: +attributes: check_mode: support: full diff_mode: @@ -52,11 +51,12 @@ - The value of the increment to apply. extends_documentation_fragment: - community.general.ldap.documentation + - community.general.attributes ''' -EXAMPLES = r""" +EXAMPLES = r''' - name: Increments uidNumber 1 Number for example.com community.general.ldap_inc: dn: "cn=uidNext,ou=unix-management,dc=example,dc=com" @@ -69,12 +69,12 @@ dn: "cn=john,ou=posix-users,dc=example,dc=com" state: present attributes: - - uidNumber: "{{ ldap_uidNumber_sequence.value }} + - uidNumber: "{{ ldap_uidNumber_sequence.value }}" when: ldap_uidNumber_sequence.incremented -""" +''' -RETURN = r""" +RETURN = r''' result: description: - attribute received the attributeType changed @@ -85,7 +85,7 @@ - incremented: true - attribute: "uidNumber" - value: "2" -""" +''' import traceback @@ -96,7 +96,6 @@ LDAP_IMP_ERR = None try: import ldap - from ldap.controls.readentry import ReadEntryControl HAS_LDAP = True except ImportError: @@ -115,7 +114,8 @@ def inc(self): return [(ldap.MOD_INCREMENT, self.attr, [to_bytes(str(self.increment))])] def serverControls(self): - return [ldap.controls.readentry.PostReadControl(attrList=[ self.attr ])] + return [ldap.controls.readentry.PostReadControl(attrList=[self.attr])] + def main(): module = AnsibleModule( @@ -142,7 +142,7 @@ def main(): changed = True if not module.check_mode: - _, _, _, resp_ctrls = mod.connection.modify_ext_s( + i0, i1, i2, resp_ctrls = mod.connection.modify_ext_s( dn=mod.dn, modlist=mod.inc(), serverctrls=mod.serverControls(), From a2d88c1c74c4adb22e175e3bfb82fa410b46b5a1 Mon Sep 17 00:00:00 2001 From: Philippe Duveau Date: Fri, 20 Dec 2024 20:52:06 +0100 Subject: [PATCH 5/6] Update plugins/modules/ldap_inc.py Fully aligned with that Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> --- plugins/modules/ldap_inc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/ldap_inc.py b/plugins/modules/ldap_inc.py index c79b643953d..5548fdfba79 100644 --- a/plugins/modules/ldap_inc.py +++ b/plugins/modules/ldap_inc.py @@ -142,7 +142,7 @@ def main(): changed = True if not module.check_mode: - i0, i1, i2, resp_ctrls = mod.connection.modify_ext_s( + dummy, dummy, dummy, resp_ctrls = mod.connection.modify_ext_s( dn=mod.dn, modlist=mod.inc(), serverctrls=mod.serverControls(), From 0cf70f2360631f66735ffd35bab0dc75edea2a88 Mon Sep 17 00:00:00 2001 From: Philippe Duveau Date: Sat, 21 Dec 2024 00:32:08 +0100 Subject: [PATCH 6/6] Update plugins/modules/ldap_inc.py Co-authored-by: Felix Fontein --- plugins/modules/ldap_inc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/ldap_inc.py b/plugins/modules/ldap_inc.py index 5548fdfba79..e401208d444 100644 --- a/plugins/modules/ldap_inc.py +++ b/plugins/modules/ldap_inc.py @@ -18,7 +18,7 @@ DOCUMENTATION = r''' --- module: ldap_inc -short_description: Use the Modify-Increment ldap V3 feature to increment an attribute value +short_description: Use the Modify-Increment LDAP V3 feature to increment an attribute value description: - Atomically increments the value of an attribute and return its new value. notes: