Skip to content

Added paging support with pageSize option for LDAP #240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 62 additions & 5 deletions python/multicorn/ldapfdw.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
``bindpwd`` (string)
The credentials for the binddn.

``pageSize`` (integer)
Paged mode size (default: 1000)


Usage Example
-------------

Expand Down Expand Up @@ -79,6 +83,48 @@
[email protected] | Some Test User |
(3 rows)

.. code-block:: bash

CREATE FOREIGN TABLE ldapgroups (
entryDN character varying,
cn character varying,
ou character varying[],
gidnumber integer,
memberuid character varying[],
objectClass character varying[]
) server ldap_srv options (
uri 'ldap://localhost',
path 'ou=Groups,dc=example,dc=com',
scope 'sub',
objectclass '*',
pagesize '200'
);

SELECT cn,gidnumber,objectClass FROM ldapgroups WHERE cn = 'Administrators';

.. code-block:: bash

cn | gidnumber | objectclass
----------------+-----------+--------------------------------
Administrators | 544 | {posixGroup,sambaGroupMapping}
(1 row)

.. code-block:: bash

SELECT unnest(objectClass) as cls,count(entryDN) as cnt FROM ldapgroups GROUP BY cls;

.. code-block:: bash

cls | cnt
---------------------+-----
domainRelatedObject | 1
organizationalUnit | 4
groupOfUniqueNames | 1
top | 10
sambaGroupMapping | 10
posixGroup | 16
(6 rows)

"""

from . import ForeignDataWrapper
Expand Down Expand Up @@ -110,6 +156,7 @@ class LdapFdw(ForeignDataWrapper):
scope -- the ldap scope (one, sub or base)
binddn -- the ldap bind DN (ex: 'cn=Admin,dc=example,dc=com')
bindpwd -- the ldap bind Password
pageSize -- the max entries per page (default: 1000 per Active Directory)

"""

Expand All @@ -123,7 +170,8 @@ def __init__(self, fdw_options, fdw_columns):
ldap3.Server(self.ldapuri),
user=fdw_options.get("binddn", None),
password=fdw_options.get("bindpwd", None),
client_strategy=ldap3.RESTARTABLE if ldap3.version.__version__ > '2.0.0' else ldap3.STRATEGY_SYNC_RESTARTABLE)
client_strategy=ldap3.RESTARTABLE if ldap3.version.__version__ > '2.0.0' else ldap3.STRATEGY_SYNC_RESTARTABLE,
return_empty_attributes=False)
self.path = fdw_options["path"]
self.scope = self.parse_scope(fdw_options.get("scope", None))
self.object_class = fdw_options["objectclass"]
Expand All @@ -133,6 +181,7 @@ def __init__(self, fdw_options, fdw_columns):
self.array_columns = [
col.column_name for name, col in self.field_definitions.items()
if col.type_name.endswith('[]')]
self.page_size = int(fdw_options["pagesize"]) if "pagesize" in fdw_options else 1000

def execute(self, quals, columns):
request = unicode_("(objectClass=%s)") % self.object_class
Expand All @@ -150,22 +199,30 @@ def execute(self, quals, columns):
val = qual.value
request = unicode_("(&%s(%s=%s))") % (
request, qual.field_name, val)
self.ldap.search(
# Always use paged search mode to read data
generator = self.ldap.extend.standard.paged_search(
self.path, request, self.scope,
attributes=list(self.field_definitions))
for entry in self.ldap.response:
attributes=list(self.field_definitions),
paged_size=self.page_size,
generator=True)
for entry in generator:
# Case insensitive lookup for the attributes
litem = dict()
for key, value in entry["attributes"].items():
if key.lower() in self.field_definitions:
pgcolname = self.field_definitions[key.lower()].column_name
if ldap3.version.__version__ > '2.0.0':
value = value
if pgcolname in self.array_columns:
value = value
else:
value = value[0] if isinstance(value, list) else value
else:
if pgcolname in self.array_columns:
value = value
else:
value = value[0]
if not value:
value = None
litem[pgcolname] = value
yield litem

Expand Down