summaryrefslogtreecommitdiff
path: root/ldap3/extend
diff options
context:
space:
mode:
Diffstat (limited to 'ldap3/extend')
-rw-r--r--ldap3/extend/__init__.py29
-rw-r--r--ldap3/extend/microsoft/addMembersToGroups.py174
-rw-r--r--ldap3/extend/microsoft/dirSync.py2
-rw-r--r--ldap3/extend/microsoft/modifyPassword.py2
-rw-r--r--ldap3/extend/microsoft/removeMembersFromGroups.py185
-rw-r--r--ldap3/extend/microsoft/unlockAccount.py4
-rw-r--r--ldap3/extend/novell/addMembersToGroups.py2
-rw-r--r--ldap3/extend/novell/checkGroupsMemberships.py2
-rw-r--r--ldap3/extend/novell/endTransaction.py2
-rw-r--r--ldap3/extend/novell/getBindDn.py2
-rw-r--r--ldap3/extend/novell/listReplicas.py4
-rw-r--r--ldap3/extend/novell/nmasGetUniversalPassword.py16
-rw-r--r--ldap3/extend/novell/nmasSetUniversalPassword.py2
-rw-r--r--ldap3/extend/novell/partition_entry_count.py2
-rw-r--r--ldap3/extend/novell/removeMembersFromGroups.py2
-rw-r--r--ldap3/extend/novell/replicaInfo.py2
-rw-r--r--ldap3/extend/novell/startTransaction.py2
-rw-r--r--ldap3/extend/operation.py2
-rw-r--r--ldap3/extend/standard/PagedSearch.py35
-rw-r--r--ldap3/extend/standard/PersistentSearch.py28
-rw-r--r--ldap3/extend/standard/modifyPassword.py4
-rw-r--r--ldap3/extend/standard/whoAmI.py4
22 files changed, 292 insertions, 215 deletions
diff --git a/ldap3/extend/__init__.py b/ldap3/extend/__init__.py
index 24f426e..32795ef 100644
--- a/ldap3/extend/__init__.py
+++ b/ldap3/extend/__init__.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2014 - 2018 Giovanni Cannata
+# Copyright 2014 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
@@ -169,6 +169,33 @@ class StandardExtendedOperations(ExtendedOperationContainer):
streaming,
callback)
+ def funnel_search(self,
+ search_base='',
+ search_filter='',
+ search_scope=SUBTREE,
+ dereference_aliases=DEREF_NEVER,
+ attributes=ALL_ATTRIBUTES,
+ size_limit=0,
+ time_limit=0,
+ controls=None,
+ streaming=False,
+ callback=None
+ ):
+ return PersistentSearch(self._connection,
+ search_base,
+ search_filter,
+ search_scope,
+ dereference_aliases,
+ attributes,
+ size_limit,
+ time_limit,
+ controls,
+ None,
+ None,
+ None,
+ streaming,
+ callback)
+
class NovellExtendedOperations(ExtendedOperationContainer):
def get_bind_dn(self, controls=None):
diff --git a/ldap3/extend/microsoft/addMembersToGroups.py b/ldap3/extend/microsoft/addMembersToGroups.py
index 28c409f..eaf6cfd 100644
--- a/ldap3/extend/microsoft/addMembersToGroups.py
+++ b/ldap3/extend/microsoft/addMembersToGroups.py
@@ -1,81 +1,93 @@
-"""
-"""
-
-# Created on 2016.12.26
-#
-# Author: Giovanni Cannata
-#
-# Copyright 2016 - 2018 Giovanni Cannata
-#
-# This file is part of ldap3.
-#
-# ldap3 is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# ldap3 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with ldap3 in the COPYING and COPYING.LESSER files.
-# If not, see <http://www.gnu.org/licenses/>.
-from ...core.exceptions import LDAPInvalidDnError
-from ... import SEQUENCE_TYPES, MODIFY_ADD, BASE, DEREF_NEVER
-
-
-def ad_add_members_to_groups(connection,
- members_dn,
- groups_dn,
- fix=True):
- """
- :param connection: a bound Connection object
- :param members_dn: the list of members to add to groups
- :param groups_dn: the list of groups where members are to be added
- :param fix: checks for group existence and already assigned members
- :return: a boolean where True means that the operation was successful and False means an error has happened
- Establishes users-groups relations following the Active Directory rules: users are added to the member attribute of groups.
- Raises LDAPInvalidDnError if members or groups are not found in the DIT.
- """
-
- if not isinstance(members_dn, SEQUENCE_TYPES):
- members_dn = [members_dn]
-
- if not isinstance(groups_dn, SEQUENCE_TYPES):
- groups_dn = [groups_dn]
-
- error = False
- for group in groups_dn:
- if fix: # checks for existance of group and for already assigned members
- result = connection.search(group, '(objectclass=*)', BASE, dereference_aliases=DEREF_NEVER, attributes=['member'])
-
- if not connection.strategy.sync:
- response, result = connection.get_response(result)
- else:
- response, result = connection.response, connection.result
-
- if not result['description'] == 'success':
- raise LDAPInvalidDnError(group + ' not found')
-
- existing_members = response[0]['attributes']['member'] if 'member' in response[0]['attributes'] else []
- existing_members = [element.lower() for element in existing_members]
- else:
- existing_members = []
-
- changes = dict()
- member_to_add = [element for element in members_dn if element.lower() not in existing_members]
- if member_to_add:
- changes['member'] = (MODIFY_ADD, member_to_add)
- if changes:
- result = connection.modify(group, changes)
- if not connection.strategy.sync:
- _, result = connection.get_response(result)
- else:
- result = connection.result
- if result['description'] != 'success':
- error = True
- break
-
- return not error # returns True if no error is raised in the LDAP operations
+"""
+"""
+
+# Created on 2016.12.26
+#
+# Author: Giovanni Cannata
+#
+# Copyright 2016 - 2020 Giovanni Cannata
+#
+# This file is part of ldap3.
+#
+# ldap3 is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# ldap3 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with ldap3 in the COPYING and COPYING.LESSER files.
+# If not, see <http://www.gnu.org/licenses/>.
+
+from ... import SEQUENCE_TYPES, MODIFY_ADD, BASE, DEREF_NEVER
+from ...core.exceptions import LDAPInvalidDnError, LDAPOperationsErrorResult
+from ...utils.dn import safe_dn
+
+
+def ad_add_members_to_groups(connection,
+ members_dn,
+ groups_dn,
+ fix=True,
+ raise_error=False):
+ """
+ :param connection: a bound Connection object
+ :param members_dn: the list of members to add to groups
+ :param groups_dn: the list of groups where members are to be added
+ :param fix: checks for group existence and already assigned members
+ :param raise_error: If the operation fails it raises an error instead of returning False
+ :return: a boolean where True means that the operation was successful and False means an error has happened
+ Establishes users-groups relations following the Active Directory rules: users are added to the member attribute of groups.
+ Raises LDAPInvalidDnError if members or groups are not found in the DIT.
+ """
+
+ if not isinstance(members_dn, SEQUENCE_TYPES):
+ members_dn = [members_dn]
+
+ if not isinstance(groups_dn, SEQUENCE_TYPES):
+ groups_dn = [groups_dn]
+
+ if connection.check_names: # builds new lists with sanitized dn
+ members_dn = [safe_dn(member_dn) for member_dn in members_dn]
+ groups_dn = [safe_dn(group_dn) for group_dn in groups_dn]
+
+ error = False
+ for group in groups_dn:
+ if fix: # checks for existance of group and for already assigned members
+ result = connection.search(group, '(objectclass=*)', BASE, dereference_aliases=DEREF_NEVER,
+ attributes=['member'])
+
+ if not connection.strategy.sync:
+ response, result = connection.get_response(result)
+ else:
+ response, result = connection.response, connection.result
+
+ if not result['description'] == 'success':
+ raise LDAPInvalidDnError(group + ' not found')
+
+ existing_members = response[0]['attributes']['member'] if 'member' in response[0]['attributes'] else []
+ existing_members = [element.lower() for element in existing_members]
+ else:
+ existing_members = []
+
+ changes = dict()
+ member_to_add = [element for element in members_dn if element.lower() not in existing_members]
+ if member_to_add:
+ changes['member'] = (MODIFY_ADD, member_to_add)
+ if changes:
+ result = connection.modify(group, changes)
+ if not connection.strategy.sync:
+ _, result = connection.get_response(result)
+ else:
+ result = connection.result
+ if result['description'] != 'success':
+ error = True
+ result_error_params = ['result', 'description', 'dn', 'message']
+ if raise_error:
+ raise LDAPOperationsErrorResult([(k, v) for k, v in result.items() if k in result_error_params])
+ break
+
+ return not error # returns True if no error is raised in the LDAP operations
diff --git a/ldap3/extend/microsoft/dirSync.py b/ldap3/extend/microsoft/dirSync.py
index cb18e7a..db403a1 100644
--- a/ldap3/extend/microsoft/dirSync.py
+++ b/ldap3/extend/microsoft/dirSync.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2015 - 2018 Giovanni Cannata
+# Copyright 2015 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
diff --git a/ldap3/extend/microsoft/modifyPassword.py b/ldap3/extend/microsoft/modifyPassword.py
index 4a17fb0..0bf1c06 100644
--- a/ldap3/extend/microsoft/modifyPassword.py
+++ b/ldap3/extend/microsoft/modifyPassword.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2015 - 2018 Giovanni Cannata
+# Copyright 2015 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
diff --git a/ldap3/extend/microsoft/removeMembersFromGroups.py b/ldap3/extend/microsoft/removeMembersFromGroups.py
index 1b7feb3..0998713 100644
--- a/ldap3/extend/microsoft/removeMembersFromGroups.py
+++ b/ldap3/extend/microsoft/removeMembersFromGroups.py
@@ -1,93 +1,92 @@
-"""
-"""
-
-# Created on 2016.12.26
-#
-# Author: Giovanni Cannata
-#
-# Copyright 2016 - 2018 Giovanni Cannata
-#
-# This file is part of ldap3.
-#
-# ldap3 is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# ldap3 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with ldap3 in the COPYING and COPYING.LESSER files.
-# If not, see <http://www.gnu.org/licenses/>.
-from ...core.exceptions import LDAPInvalidDnError
-from ... import SEQUENCE_TYPES, MODIFY_DELETE, BASE, DEREF_NEVER
-from ...utils.dn import safe_dn
-
-
-def ad_remove_members_from_groups(connection,
- members_dn,
- groups_dn,
- fix):
- """
- :param connection: a bound Connection object
- :param members_dn: the list of members to remove from groups
- :param groups_dn: the list of groups where members are to be removed
- :param fix: checks for group existence and existing members
- :return: a boolean where True means that the operation was successful and False means an error has happened
- Removes users-groups relations following the Activwe Directory rules: users are removed from groups' member attribute
-
- """
- if not isinstance(members_dn, SEQUENCE_TYPES):
- members_dn = [members_dn]
-
- if not isinstance(groups_dn, SEQUENCE_TYPES):
- groups_dn = [groups_dn]
-
- if connection.check_names: # builds new lists with sanitized dn
- safe_members_dn = []
- safe_groups_dn = []
- for member_dn in members_dn:
- safe_members_dn.append(safe_dn(member_dn))
- for group_dn in groups_dn:
- safe_groups_dn.append(safe_dn(group_dn))
-
- members_dn = safe_members_dn
- groups_dn = safe_groups_dn
-
- error = False
-
- for group in groups_dn:
- if fix: # checks for existance of group and for already assigned members
- result = connection.search(group, '(objectclass=*)', BASE, dereference_aliases=DEREF_NEVER, attributes=['member'])
-
- if not connection.strategy.sync:
- response, result = connection.get_response(result)
- else:
- response, result = connection.response, connection.result
-
- if not result['description'] == 'success':
- raise LDAPInvalidDnError(group + ' not found')
-
- existing_members = response[0]['attributes']['member'] if 'member' in response[0]['attributes'] else []
- else:
- existing_members = members_dn
-
- existing_members = [element.lower() for element in existing_members]
- changes = dict()
- member_to_remove = [element for element in members_dn if element.lower() in existing_members]
- if member_to_remove:
- changes['member'] = (MODIFY_DELETE, member_to_remove)
- if changes:
- result = connection.modify(group, changes)
- if not connection.strategy.sync:
- _, result = connection.get_response(result)
- else:
- result = connection.result
- if result['description'] != 'success':
- error = True
- break
-
- return not error
+"""
+"""
+
+# Created on 2016.12.26
+#
+# Author: Giovanni Cannata
+#
+# Copyright 2016 - 2020 Giovanni Cannata
+#
+# This file is part of ldap3.
+#
+# ldap3 is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# ldap3 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with ldap3 in the COPYING and COPYING.LESSER files.
+# If not, see <http://www.gnu.org/licenses/>.
+
+from ...core.exceptions import LDAPInvalidDnError, LDAPOperationsErrorResult
+from ... import SEQUENCE_TYPES, MODIFY_DELETE, BASE, DEREF_NEVER
+from ...utils.dn import safe_dn
+
+
+def ad_remove_members_from_groups(connection,
+ members_dn,
+ groups_dn,
+ fix,
+ raise_error=False):
+ """
+ :param connection: a bound Connection object
+ :param members_dn: the list of members to remove from groups
+ :param groups_dn: the list of groups where members are to be removed
+ :param fix: checks for group existence and existing members
+ :param raise_error: If the operation fails it raises an error instead of returning False
+ :return: a boolean where True means that the operation was successful and False means an error has happened
+ Removes users-groups relations following the Activwe Directory rules: users are removed from groups' member attribute
+
+ """
+ if not isinstance(members_dn, SEQUENCE_TYPES):
+ members_dn = [members_dn]
+
+ if not isinstance(groups_dn, SEQUENCE_TYPES):
+ groups_dn = [groups_dn]
+
+ if connection.check_names: # builds new lists with sanitized dn
+ members_dn = [safe_dn(member_dn) for member_dn in members_dn]
+ groups_dn = [safe_dn(group_dn) for group_dn in groups_dn]
+
+ error = False
+
+ for group in groups_dn:
+ if fix: # checks for existance of group and for already assigned members
+ result = connection.search(group, '(objectclass=*)', BASE, dereference_aliases=DEREF_NEVER, attributes=['member'])
+
+ if not connection.strategy.sync:
+ response, result = connection.get_response(result)
+ else:
+ response, result = connection.response, connection.result
+
+ if not result['description'] == 'success':
+ raise LDAPInvalidDnError(group + ' not found')
+
+ existing_members = response[0]['attributes']['member'] if 'member' in response[0]['attributes'] else []
+ else:
+ existing_members = members_dn
+
+ existing_members = [element.lower() for element in existing_members]
+ changes = dict()
+ member_to_remove = [element for element in members_dn if element.lower() in existing_members]
+ if member_to_remove:
+ changes['member'] = (MODIFY_DELETE, member_to_remove)
+ if changes:
+ result = connection.modify(group, changes)
+ if not connection.strategy.sync:
+ _, result = connection.get_response(result)
+ else:
+ result = connection.result
+ if result['description'] != 'success':
+ error = True
+ result_error_params = ['result', 'description', 'dn', 'message']
+ if raise_error:
+ raise LDAPOperationsErrorResult([(k, v) for k, v in result.items() if k in result_error_params])
+ break
+
+ return not error
diff --git a/ldap3/extend/microsoft/unlockAccount.py b/ldap3/extend/microsoft/unlockAccount.py
index 60b9ed3..bc59b58 100644
--- a/ldap3/extend/microsoft/unlockAccount.py
+++ b/ldap3/extend/microsoft/unlockAccount.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2015 - 2018 Giovanni Cannata
+# Copyright 2015 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
@@ -34,7 +34,7 @@ def ad_unlock_account(connection, user_dn, controls=None):
if connection.check_names:
user_dn = safe_dn(user_dn)
result = connection.modify(user_dn,
- {'lockoutTime': [(MODIFY_REPLACE, [0])]},
+ {'lockoutTime': [(MODIFY_REPLACE, ['0'])]},
controls)
if not connection.strategy.sync:
diff --git a/ldap3/extend/novell/addMembersToGroups.py b/ldap3/extend/novell/addMembersToGroups.py
index 5583549..d649dc8 100644
--- a/ldap3/extend/novell/addMembersToGroups.py
+++ b/ldap3/extend/novell/addMembersToGroups.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2016 - 2018 Giovanni Cannata
+# Copyright 2016 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
diff --git a/ldap3/extend/novell/checkGroupsMemberships.py b/ldap3/extend/novell/checkGroupsMemberships.py
index 1013fde..c51dbf2 100644
--- a/ldap3/extend/novell/checkGroupsMemberships.py
+++ b/ldap3/extend/novell/checkGroupsMemberships.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2016 - 2018 Giovanni Cannata
+# Copyright 2016 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
diff --git a/ldap3/extend/novell/endTransaction.py b/ldap3/extend/novell/endTransaction.py
index 0e9a58c..18bc041 100644
--- a/ldap3/extend/novell/endTransaction.py
+++ b/ldap3/extend/novell/endTransaction.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2016 - 2018 Giovanni Cannata
+# Copyright 2016 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
diff --git a/ldap3/extend/novell/getBindDn.py b/ldap3/extend/novell/getBindDn.py
index 39fae2b..492bcdd 100644
--- a/ldap3/extend/novell/getBindDn.py
+++ b/ldap3/extend/novell/getBindDn.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2014 - 2018 Giovanni Cannata
+# Copyright 2014 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
diff --git a/ldap3/extend/novell/listReplicas.py b/ldap3/extend/novell/listReplicas.py
index fdc6d08..8ccf2ff 100644
--- a/ldap3/extend/novell/listReplicas.py
+++ b/ldap3/extend/novell/listReplicas.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2014 - 2018 Giovanni Cannata
+# Copyright 2014 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
@@ -45,6 +45,6 @@ class ListReplicas(ExtendedOperation):
def populate_result(self):
try:
- self.result['replicas'] = str(self.decoded_response['replicaList']) if self.decoded_response['replicaList'] else None
+ self.result['replicas'] = [str(replica) for replica in self.decoded_response] if self.decoded_response else None
except TypeError:
self.result['replicas'] = None
diff --git a/ldap3/extend/novell/nmasGetUniversalPassword.py b/ldap3/extend/novell/nmasGetUniversalPassword.py
index 20aa928..291ae92 100644
--- a/ldap3/extend/novell/nmasGetUniversalPassword.py
+++ b/ldap3/extend/novell/nmasGetUniversalPassword.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2014 - 2018 Giovanni Cannata
+# Copyright 2014 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
@@ -46,9 +46,11 @@ class NmasGetUniversalPassword(ExtendedOperation):
self.request_value['reqdn'] = user
def populate_result(self):
- self.result['nmasver'] = int(self.decoded_response['nmasver'])
- self.result['error'] = int(self.decoded_response['err'])
- try:
- self.result['password'] = str(self.decoded_response['passwd']) if self.decoded_response['passwd'] else None
- except TypeError:
- self.result['password'] = None
+ if self.decoded_response:
+ self.result['nmasver'] = int(self.decoded_response['nmasver'])
+ self.result['error'] = int(self.decoded_response['err'])
+ try:
+
+ self.result['password'] = str(self.decoded_response['passwd']) if self.decoded_response['passwd'].hasValue() else None
+ except TypeError:
+ self.result['password'] = None
diff --git a/ldap3/extend/novell/nmasSetUniversalPassword.py b/ldap3/extend/novell/nmasSetUniversalPassword.py
index 65ea0d6..dadab59 100644
--- a/ldap3/extend/novell/nmasSetUniversalPassword.py
+++ b/ldap3/extend/novell/nmasSetUniversalPassword.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2014 - 2018 Giovanni Cannata
+# Copyright 2014 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
diff --git a/ldap3/extend/novell/partition_entry_count.py b/ldap3/extend/novell/partition_entry_count.py
index 8218aea..3d46c7a 100644
--- a/ldap3/extend/novell/partition_entry_count.py
+++ b/ldap3/extend/novell/partition_entry_count.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2014 - 2018 Giovanni Cannata
+# Copyright 2014 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
diff --git a/ldap3/extend/novell/removeMembersFromGroups.py b/ldap3/extend/novell/removeMembersFromGroups.py
index df493ba..c46c275 100644
--- a/ldap3/extend/novell/removeMembersFromGroups.py
+++ b/ldap3/extend/novell/removeMembersFromGroups.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2016 - 2018 Giovanni Cannata
+# Copyright 2016 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
diff --git a/ldap3/extend/novell/replicaInfo.py b/ldap3/extend/novell/replicaInfo.py
index 45bd0e9..057f934 100644
--- a/ldap3/extend/novell/replicaInfo.py
+++ b/ldap3/extend/novell/replicaInfo.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2014 - 2018 Giovanni Cannata
+# Copyright 2014 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
diff --git a/ldap3/extend/novell/startTransaction.py b/ldap3/extend/novell/startTransaction.py
index 2ed21c2..6179cb0 100644
--- a/ldap3/extend/novell/startTransaction.py
+++ b/ldap3/extend/novell/startTransaction.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2016 - 2018 Giovanni Cannata
+# Copyright 2016 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
diff --git a/ldap3/extend/operation.py b/ldap3/extend/operation.py
index 9906885..c1d478c 100644
--- a/ldap3/extend/operation.py
+++ b/ldap3/extend/operation.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2014 - 2018 Giovanni Cannata
+# Copyright 2014 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
diff --git a/ldap3/extend/standard/PagedSearch.py b/ldap3/extend/standard/PagedSearch.py
index 6fb1a56..f8bc7e6 100644
--- a/ldap3/extend/standard/PagedSearch.py
+++ b/ldap3/extend/standard/PagedSearch.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2014 - 2018 Giovanni Cannata
+# Copyright 2014 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
@@ -25,7 +25,7 @@
from ... import SUBTREE, DEREF_ALWAYS
from ...utils.dn import safe_dn
-from ...core.results import DO_NOT_RAISE_EXCEPTIONS
+from ...core.results import DO_NOT_RAISE_EXCEPTIONS, RESULT_SIZE_LIMIT_EXCEEDED
from ...core.exceptions import LDAPOperationResult
from ...utils.log import log, log_enabled, ERROR, BASIC, PROTOCOL, NETWORK, EXTENDED
@@ -47,7 +47,11 @@ def paged_search_generator(connection,
search_base = safe_dn(search_base)
responses = []
- cookie = True # performs search at least one time
+ original_connection = None
+ original_auto_referrals = connection.auto_referrals
+ connection.auto_referrals = False # disable auto referrals because it cannot handle paged searches
+ cookie = True # performs search operation at least one time
+ cachekey = None # for referrals cache
while cookie:
result = connection.search(search_base,
search_filter,
@@ -69,10 +73,11 @@ def paged_search_generator(connection,
response = connection.response
result = connection.result
- if result and result['result'] not in DO_NOT_RAISE_EXCEPTIONS:
- if log_enabled(PROTOCOL):
- log(PROTOCOL, 'paged search operation result <%s> for <%s>', result, connection)
- raise LDAPOperationResult(result=result['result'], description=result['description'], dn=result['dn'], message=result['message'], response_type=result['type'])
+ if result['referrals'] and original_auto_referrals: # if rererrals are returned start over the loop with a new connection to the referral
+ if not original_connection:
+ original_connection = connection
+ _, connection, cachekey = connection.strategy.create_referral_connection(result['referrals']) # change connection to a valid referrals
+ continue
responses.extend(response)
try:
@@ -80,9 +85,25 @@ def paged_search_generator(connection,
except KeyError:
cookie = None
+ if connection.raise_exceptions and result and result['result'] not in DO_NOT_RAISE_EXCEPTIONS:
+ if log_enabled(PROTOCOL):
+ log(PROTOCOL, 'paged search operation result <%s> for <%s>', result, connection)
+ if result['result'] == RESULT_SIZE_LIMIT_EXCEEDED:
+ while responses:
+ yield responses.pop()
+ raise LDAPOperationResult(result=result['result'], description=result['description'], dn=result['dn'], message=result['message'], response_type=result['type'])
+
while responses:
yield responses.pop()
+ if original_connection:
+ connection = original_connection
+ if connection.use_referral_cache and cachekey:
+ connection.strategy.referral_cache[cachekey] = connection
+ else:
+ connection.unbind()
+
+ connection.auto_referrals = original_auto_referrals
connection.response = None
diff --git a/ldap3/extend/standard/PersistentSearch.py b/ldap3/extend/standard/PersistentSearch.py
index 62286e1..b25ec68 100644
--- a/ldap3/extend/standard/PersistentSearch.py
+++ b/ldap3/extend/standard/PersistentSearch.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2016 - 2018 Giovanni Cannata
+# Copyright 2016 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
@@ -80,7 +80,8 @@ class PersistentSearch(object):
else:
self.controls = controls
- self.controls.append(persistent_search_control(events_type, changes_only, notifications))
+ if events_type and changes_only and notifications:
+ self.controls.append(persistent_search_control(events_type, changes_only, notifications))
self.start()
def start(self):
@@ -101,9 +102,10 @@ class PersistentSearch(object):
controls=self.controls)
self.connection.strategy.persistent_search_message_id = self.message_id
- def stop(self):
+ def stop(self, unbind=True):
self.connection.abandon(self.message_id)
- self.connection.unbind()
+ if unbind:
+ self.connection.unbind()
if self.message_id in self.connection.strategy._responses:
del self.connection.strategy._responses[self.message_id]
if hasattr(self.connection.strategy, '_requests') and self.message_id in self.connection.strategy._requests: # asynchronous strategy has a dict of request that could be returned by get_response()
@@ -111,11 +113,25 @@ class PersistentSearch(object):
self.connection.strategy.persistent_search_message_id = None
self.message_id = None
- def next(self):
+ def next(self, block=False, timeout=None):
if not self.connection.strategy.streaming and not self.connection.strategy.callback:
try:
- return self.connection.strategy.events.get_nowait()
+ return self.connection.strategy.events.get(block, timeout)
except Empty:
return None
raise LDAPExtensionError('Persistent search is not accumulating events in queue')
+
+ def funnel(self, block=False, timeout=None):
+ done = False
+ while not done:
+ try:
+ entry = self.connection.strategy.events.get(block, timeout)
+ except Empty:
+ yield None
+ if entry['type'] == 'searchResEntry':
+ yield entry
+ else:
+ done = True
+
+ yield entry
diff --git a/ldap3/extend/standard/modifyPassword.py b/ldap3/extend/standard/modifyPassword.py
index 167816e..7837355 100644
--- a/ldap3/extend/standard/modifyPassword.py
+++ b/ldap3/extend/standard/modifyPassword.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2014 - 2018 Giovanni Cannata
+# Copyright 2014 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
@@ -67,6 +67,6 @@ class ModifyPassword(ExtendedOperation):
self.result[self.response_attribute] = True
else: # change was not successful, raises exception if raise_exception = True in connection or returns the operation result, error code is in result['result']
self.result[self.response_attribute] = False
- if not self.connection.raise_exceptions:
+ if self.connection.raise_exceptions:
from ...core.exceptions import LDAPOperationResult
raise LDAPOperationResult(result=self.result['result'], description=self.result['description'], dn=self.result['dn'], message=self.result['message'], response_type=self.result['type'])
diff --git a/ldap3/extend/standard/whoAmI.py b/ldap3/extend/standard/whoAmI.py
index 121e40b..a6c08a8 100644
--- a/ldap3/extend/standard/whoAmI.py
+++ b/ldap3/extend/standard/whoAmI.py
@@ -5,7 +5,7 @@
#
# Author: Giovanni Cannata
#
-# Copyright 2014 - 2018 Giovanni Cannata
+# Copyright 2014 - 2020 Giovanni Cannata
#
# This file is part of ldap3.
#
@@ -24,10 +24,10 @@
# If not, see <http://www.gnu.org/licenses/>.
# implements RFC4532
-
from ...extend.operation import ExtendedOperation
from ...utils.conv import to_unicode
+
class WhoAmI(ExtendedOperation):
def config(self):
self.request_name = '1.3.6.1.4.1.4203.1.11.3'