from ldap3 import Connection, DSA, IP_V4_PREFERRED, SAFE_SYNC, Server
from ldap3 import MODIFY_ADD, MODIFY_DELETE, MODIFY_REPLACE
from ldap3.core.exceptions import LDAPBindError
-# from ldap3.core.exceptions import LDAPCommunicationError
+from ldap3.core.exceptions import LDAPCommunicationError
from ldap3.core.exceptions import LDAPException
from ldap3.core.exceptions import LDAPSessionTerminatedByServerError
-from ldap3.core.exceptions import LDAPSocketOpenError
-from ldap3.core.exceptions import LDAPSocketReceiveError
# Own modules
from . import BaseDPXApplication
from ..config.ldap import LdapConfiguration, LdapConnectionInfo
# from ..errors import DpxAppError
from ..errors import DpxDeleteLdapItemError
+from ..errors import DpxFatalLdapError
from ..errors import DpxLdapError
from ..errors import DpxLdapParseError
-from ..errors import DpxLdapReadError
+from ..errors import DpxLdapSearchError
from ..errors import DpxLdapSessionError
from ..errors import DpxWriteLdapItemError
from ..xlate import XLATOR, format_list
-__version__ = '1.2.0'
+__version__ = '1.3.0'
LOG = logging.getLogger(__name__)
_ = XLATOR.gettext
del self.ldap_server[inst]
# -------------------------------------------------------------------------
- def get_all_entries(self, inst, base_dn=None, ldap_filter=None, attributes=None, scope=None):
+ def search(
+ self, inst, search_base=None, ldap_filter=None, attributes=None, scope=None,
+ retries=None, wait_on_error=None, operational_attributes=False):
"""
- Get all LDAP entries bellow the given BaseDN and the given LDAP filter.
+ Execute a LDAP search with the given parameters.
- If no attributes are given, all attributes are given back.
- The result is a hash with the DNs if the resulting entries as keys, and a hash
- with the resulting attributes as values.
+ Returns a tuple with:
+ * status
+ * result
+ * response
"""
connect_info = self.cfg.ldap_connection[inst]
if scope is None:
scope = SUBTREE
- result = {}
+ rec_out = ''
+ if scope == SUBTREE:
+ rec_out = _('recursive') + ' '
- if not base_dn:
- base_dn = connect_info.base_dn
+ if not search_base:
+ search_base = connect_info.base_dn
if attributes is None:
attributes = [ALL_ATTRIBUTES]
if ldap_filter is None:
ldap_filter = '(objectClass=*)'
+ if retries is None:
+ retries = self.retries_on_conn_error
+
+ if wait_on_error is None:
+ wait_on_error = self.wait_on_conn_error
+
if self.verbose > 2:
msg = _(
- 'Searching in {uri}/{bdn} for all entries with filter {fltr!r}, '
- 'giving attributes:').format(uri=connect_info.url, bdn=base_dn, fltr=ldap_filter)
+ 'Searching in {uri}/{bdn} {rec}for all entries with filter {fltr!r}, '
+ 'giving attributes:').format(
+ uri=connect_info.url, bdn=search_base, rec=rec_out, fltr=ldap_filter)
msg += ' ' + format_list(attributes, do_repr=True)
LOG.debug(msg)
+ cur_try = 0
+ search_status = None
+ search_result = None
+ search_response = None
+ result = None
+
+ while True:
+
+ cur_try += 1
+ result = self._search(
+ inst, cur_try=cur_try, retries=retries, search_base=search_base, scope=scope,
+ attributes=attributes, op_attrs=operational_attributes, ldap_filter=ldap_filter,
+ time_limit=self.cfg.ldap_timeout, wait_on_error=wait_on_error)
+
+ if result is not None:
+ break
+
+ search_status = result[0]
+ search_result = result[1]
+ search_response = result[2]
+
+ if search_status:
+ if self.verbose > 4:
+ LOG.debug(_('Result of searching:') + ' ' + pp(search_result))
+ if self.verbose > 3:
+ LOG.debug(_('Got a response entry:') + '\n' + pp(search_response[0]))
+
+ else:
+ if self.verbose > 3:
+ LOG.debug(_('Search was not successful.'))
+
+ return result
+
+ # -------------------------------------------------------------------------
+ def _search(
+ self, inst, cur_try, retries, search_base, scope, attributes,
+ op_attrs, ldap_filter, time_limit, wait_on_error):
+ """Execute underlaying LDAP search."""
+ e_cls = None
+ e_msg = None
+ search_status = None
+ search_result = None
+ search_response = None
+
+ if self.verbose > 2:
+ LOG.debug(_('Try number {} for searching ...').format(cur_try))
+
if inst not in self.ldap_connection:
self.connect_instance(inst)
ldap = self.ldap_connection[inst]
try:
- req_status, req_result, req_response, req_whatever = ldap.search(
- search_base=base_dn, search_scope=scope, attributes=attributes,
- search_filter=ldap_filter, time_limit=self.cfg.ldap_timeout)
+ search_status, search_result, search_response, whatever = ldap.search(
+ search_base=search_base, search_scope=scope, attributes=attributes,
+ get_operational_attributes=op_attrs, search_filter=ldap_filter,
+ time_limit=time_limit)
+
+ except LDAPCommunicationError as e:
+ e_msg = str(e)
+ e_cls = e.__class__.__name__
+ if cur_try > retries:
+ msg = _('Got a {cls} on searching in LDAP instance {i!r}:').format(
+ cls=e_cls, i=inst) + ' ' + str(e)
+ raise DpxLdapSearchError(msg)
+ LOG.debug(_('Waiting because of a {}:').format(e_cls) + ' ' + e_msg)
+ time.sleep(wait_on_error)
+ return None
+
+ except LDAPSessionTerminatedByServerError as e:
+ msg = _('Session terminated by server on searching on instance {i!r}:').format(
+ i=inst) + ' ' + str(e)
+ raise DpxLdapSessionError(msg)
+
+ except LDAPException as e:
+ msg = _('Got a {cls} on searching in LDAP instance {i!r}:').format(
+ cls=e.__class__.__name__, i=inst) + ' ' + str(e)
+ raise DpxFatalLdapError(msg)
+
finally:
if not self.single_session:
self.disconnect_instance(inst)
- if req_status:
- if self.verbose > 4:
- LOG.debug(_('Result of searching:') + '\n' + pp(req_result))
+ return (search_status, search_result, search_response)
- for entry in req_response:
+ # -------------------------------------------------------------------------
+ def get_all_entries(self, inst, base_dn=None, ldap_filter=None, attributes=None, scope=None):
+ """
+ Get all LDAP entries bellow the given BaseDN and the given LDAP filter.
+
+ If no attributes are given, all attributes are given back.
+ The result is a hash with the DNs if the resulting entries as keys, and a hash
+ with the resulting attributes as values.
+ """
+ connect_info = self.cfg.ldap_connection[inst]
+
+ result = {}
+ search_status = None
+ search_result = None
+ search_response = None
+
+ if not base_dn:
+ base_dn = connect_info.base_dn
+
+ (search_status, search_result, search_response) = self.search(
+ inst, search_base=base_dn, ldap_filter=ldap_filter, attributes=attributes)
+
+ if search_status:
+ for entry in search_response:
dn = entry['dn']
if self.verbose > 3:
LOG.debug(_('Found entry {!r}.').format(dn))
def get_entry(self, dn, inst, attributes=None, operational_attributes=False, tries=3):
"""Get an complete LDAP entry by the given DN in the given instance."""
connect_info = self.cfg.ldap_connection[inst]
-
- if attributes is None:
- attributes = [ALL_ATTRIBUTES]
-
- sfilter = '(objectClass=*)'
-
- result = None
-
if self.verbose > 1:
msg = _('Searching DN {dn!r} in {uri}.').format(dn=dn, uri=connect_info.url)
LOG.debug(msg)
- cur_try = 0
- e_msg = None
- while cur_try < tries:
-
- if inst not in self.ldap_connection:
- self.connect_instance(inst)
- ldap = self.ldap_connection[inst]
- e_msg = None
- cur_try += 1
- try:
- req_status, req_result, req_response, req_whatever = ldap.search(
- search_base=dn, search_scope=BASE, attributes=attributes,
- get_operational_attributes=operational_attributes, search_filter=sfilter,
- time_limit=self.cfg.ldap_timeout)
-
- except (LDAPSocketReceiveError, LDAPSocketOpenError) as e:
- e_msg = str(e)
- if cur_try >= tries:
- break
- LOG.debug(_('Waiting because of a failing read operation.'))
- time.sleep(self.wait_on_conn_error)
-
- except LDAPSessionTerminatedByServerError as e:
- msg = _('Session terminated on reading entry {dn!r} from instance {i!r}:').format(
- dn=dn, i=inst) + ' ' + str(e)
- raise DpxLdapSessionError(msg)
-
- finally:
- if not self.single_session:
- self.disconnect_instance(inst)
-
- if e_msg:
- msg = _('Error on reading entry {dn!r} from instance {inst!r}:').format(
- dn=dn, inst=inst) + ' ' + e_msg
- raise DpxLdapReadError(msg)
+ result = None
- if req_status:
- if self.verbose > 4:
- msg = _('Result of searching for DN {dn!r}:').format(dn=dn)
- LOG.debug(msg + ' ' + pp(req_result))
- result = req_response[0]
- if self.verbose > 3:
- LOG.debug(_('Got a response entry:') + '\n' + pp(result))
+ (search_status, search_result, search_response) = self.search(
+ inst=inst, search_base=dn, attributes=attributes, scope=BASE,
+ operational_attributes=operational_attributes)
+ if search_status:
+ result = search_response[0]
else:
if self.verbose > 3:
- msg = _('Entry with DN {dn!r} not found in {uri}.').format(
- dn=dn, uri=connect_info.url)
+ msg = _('Entry with DN {dn!r} not found in {i!r} ({uri}).').format(
+ dn=dn, i=inst, uri=connect_info.url)
LOG.debug(msg)
return result