Skip to content

Commit

Permalink
plugins/modules/ldap_search: Add support for multipage searches (#6648)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* Simplify if statement to reduce negatives

* Apply suggestions from code review

Co-authored-by: Felix Fontein <[email protected]>

---------

Co-authored-by: Felix Fontein <[email protected]>
  • Loading branch information
Gnonthgol and felixfontein authored Jun 15, 2023
1 parent f3ecf4c commit 8801463
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 15 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/6648_ldap_search_page_size.yml
Original file line number Diff line number Diff line change
@@ -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).
50 changes: 35 additions & 15 deletions plugins/modules/ldap_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@
description:
- Set to C(true) to return the full attribute schema of entries, not
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:
description:
- If provided, all attribute values returned that are listed in this option
Expand Down Expand Up @@ -133,6 +141,7 @@ def main():
filter=dict(type='str', default='(objectClass=*)'),
attrs=dict(type='list', elements='str'),
schema=dict(type='bool', default=False),
page_size=dict(type='int', default=0),
base64_attributes=dict(type='list', elements='str'),
),
supports_check_mode=True,
Expand Down Expand Up @@ -181,6 +190,7 @@ def __init__(self, module):

self.filterstr = self.module.params['filter']
self.attrlist = []
self.page_size = self.module.params['page_size']
self._load_scope()
self._load_attrs()
self._load_schema()
Expand Down Expand Up @@ -210,22 +220,32 @@ def main(self):
self.module.exit_json(changed=False, results=results)

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:
results = self.connection.search_s(
self.dn,
self.scope,
filterstr=self.filterstr,
attrlist=self.attrlist,
attrsonly=self.attrsonly
)
ldap_entries = []
for result in results:
if isinstance(result[1], dict):
if self.schema:
ldap_entries.append(dict(dn=result[0], attrs=list(result[1].keys())))
else:
ldap_entries.append(_extract_entry(result[0], result[1], self._base64_attributes))
return ldap_entries
while True:
response = self.connection.search_ext(
self.dn,
self.scope,
filterstr=self.filterstr,
attrlist=self.attrlist,
attrsonly=self.attrsonly,
serverctrls=controls,
)
rtype, results, rmsgid, serverctrls = self.connection.result3(response)
for result in results:
if isinstance(result[1], dict):
if self.schema:
ldap_entries.append(dict(dn=result[0], attrs=list(result[1].keys())))
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:
self.module.fail_json(msg="Base not found: {0}".format(self.dn))

Expand Down
14 changes: 14 additions & 0 deletions tests/integration/targets/ldap_search/tasks/tests/basic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,17 @@
- output is not failed
- output.results | length == 1
- 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
24 changes: 24 additions & 0 deletions tests/integration/targets/ldap_search/tasks/tests/pages.yml
Original file line number Diff line number Diff line change
@@ -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
25 changes: 25 additions & 0 deletions tests/integration/targets/ldap_search/tasks/tests/schema.yml
Original file line number Diff line number Diff line change
@@ -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 }}"
18 changes: 18 additions & 0 deletions tests/integration/targets/setup_openldap/files/initial_config.ldif
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,21 @@ displayName: LDAP Test
userPassword: test1pass!
mail: [email protected]
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: [email protected]
sn: Test

0 comments on commit 8801463

Please sign in to comment.