1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

plugins/modules/ldap_search: Add support for multipage searches (#6648)

* Add more integration tests for ldap_search

* Add new page_size option to ldap_search

* Add changelog fragment

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* Simplify if statement to reduce negatives

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
Gnonthgol 2023-06-15 07:19:55 +02:00 committed by GitHub
parent f3ecf4c7f8
commit 8801463575
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 118 additions and 15 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- ldap_search - add a new ``page_size`` option to enable paged searches (https://github.com/ansible-collections/community.general/pull/6648).

View file

@ -61,6 +61,14 @@ options:
description: description:
- Set to C(true) to return the full attribute schema of entries, not - Set to C(true) to return the full attribute schema of entries, not
their attribute values. Overrides I(attrs) when provided. their attribute values. Overrides I(attrs) when provided.
page_size:
default: 0
type: int
description:
- The page size when performing a simple paged result search (RFC 2696).
This setting can be tuned to reduce issues with timeouts and server limits.
- Setting the page size to V(0) (default) disables paged searching.
version_added: 7.1.0
base64_attributes: base64_attributes:
description: description:
- If provided, all attribute values returned that are listed in this option - If provided, all attribute values returned that are listed in this option
@ -133,6 +141,7 @@ def main():
filter=dict(type='str', default='(objectClass=*)'), filter=dict(type='str', default='(objectClass=*)'),
attrs=dict(type='list', elements='str'), attrs=dict(type='list', elements='str'),
schema=dict(type='bool', default=False), schema=dict(type='bool', default=False),
page_size=dict(type='int', default=0),
base64_attributes=dict(type='list', elements='str'), base64_attributes=dict(type='list', elements='str'),
), ),
supports_check_mode=True, supports_check_mode=True,
@ -181,6 +190,7 @@ class LdapSearch(LdapGeneric):
self.filterstr = self.module.params['filter'] self.filterstr = self.module.params['filter']
self.attrlist = [] self.attrlist = []
self.page_size = self.module.params['page_size']
self._load_scope() self._load_scope()
self._load_attrs() self._load_attrs()
self._load_schema() self._load_schema()
@ -210,22 +220,32 @@ class LdapSearch(LdapGeneric):
self.module.exit_json(changed=False, results=results) self.module.exit_json(changed=False, results=results)
def perform_search(self): def perform_search(self):
ldap_entries = []
controls = []
if self.page_size > 0:
controls.append(ldap.controls.libldap.SimplePagedResultsControl(True, size=self.page_size, cookie=''))
try: try:
results = self.connection.search_s( while True:
self.dn, response = self.connection.search_ext(
self.scope, self.dn,
filterstr=self.filterstr, self.scope,
attrlist=self.attrlist, filterstr=self.filterstr,
attrsonly=self.attrsonly attrlist=self.attrlist,
) attrsonly=self.attrsonly,
ldap_entries = [] serverctrls=controls,
for result in results: )
if isinstance(result[1], dict): rtype, results, rmsgid, serverctrls = self.connection.result3(response)
if self.schema: for result in results:
ldap_entries.append(dict(dn=result[0], attrs=list(result[1].keys()))) if isinstance(result[1], dict):
else: if self.schema:
ldap_entries.append(_extract_entry(result[0], result[1], self._base64_attributes)) ldap_entries.append(dict(dn=result[0], attrs=list(result[1].keys())))
return ldap_entries else:
ldap_entries.append(_extract_entry(result[0], result[1], self._base64_attributes))
cookies = [c.cookie for c in serverctrls if c.controlType == ldap.controls.libldap.SimplePagedResultsControl.controlType]
if self.page_size > 0 and cookies and cookies[0]:
controls[0].cookie = cookies[0]
else:
return ldap_entries
except ldap.NO_SUCH_OBJECT: except ldap.NO_SUCH_OBJECT:
self.module.fail_json(msg="Base not found: {0}".format(self.dn)) self.module.fail_json(msg="Base not found: {0}".format(self.dn))

View file

@ -23,3 +23,17 @@
- output is not failed - output is not failed
- output.results | length == 1 - output.results | length == 1
- output.results.0.displayName == "LDAP Test" - output.results.0.displayName == "LDAP Test"
- name: Test simple search for a user with no results
ldap_search:
dn: "ou=users,dc=example,dc=com"
scope: "onelevel"
filter: "(uid=nonexistent)"
ignore_errors: true
register: output
- name: assert that the output is empty
assert:
that:
- output is not failed
- output.results | length == 0

View file

@ -0,0 +1,24 @@
---
# Copyright (c) Ansible Project
# 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
- debug:
msg: Running tests/pages.yml
####################################################################
## Search ##########################################################
####################################################################
- name: Test paged search for all users
ldap_search:
dn: "ou=users,dc=example,dc=com"
scope: "onelevel"
page_size: 1
ignore_errors: true
register: output
- name: assert that the right number of results are returned
assert:
that:
- output is not failed
- output.results | length == 2

View file

@ -0,0 +1,25 @@
---
# Copyright (c) Ansible Project
# 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
- debug:
msg: Running tests/schema.yml
####################################################################
## Search ##########################################################
####################################################################
- name: Test for ldap schema output
ldap_search:
dn: "ou=users,dc=example,dc=com"
scope: "onelevel"
schema: true
ignore_errors: true
register: output
- name: Assert that the schema output is correct
assert:
that:
- output is not failed
- output.results | length >= 1
- "{{ 'displayName' in output.results.0.attrs }}"

View file

@ -21,3 +21,21 @@ displayName: LDAP Test
userPassword: test1pass! userPassword: test1pass!
mail: ldap.test@example.com mail: ldap.test@example.com
sn: Test sn: Test
dn: uid=second,ou=users,dc=example,dc=com
uid: second
uidNumber: 1112
gidNUmber: 102
objectClass: top
objectClass: posixAccount
objectClass: shadowAccount
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
loginShell: /bin/sh
homeDirectory: /home/second
cn: Second Test
gecos: Second Test
displayName: Second Test
mail: second.test@example.com
sn: Test